码迷,mamicode.com
首页 > 其他好文 > 详细

51NOD 1962 区间计数 单调栈+二分 / 线段树+扫描线

时间:2017-09-26 16:07:28      阅读:204      评论:0      收藏:0      [点我收藏+]

标签:扫描线   技术   while   1.5   view   比较   style   线段   code   

 区间计数
 
基准时间限制:1.5 秒 空间限制:262144 KB 分值: 80
 

两个数列 {An} 技术分享, {Bn} 技术分享,请求出Ans, Ans定义如下:

 

Ans:=Σni=1Σnj=i[max{Ai,Ai+1,...,Aj}=max{Bi,Bi+1,...,Bj}] 

 

注:[ ]内表达式为真,则为1,否则为0.

 
 
1N3.5×1051Ai,BiN 
 
样例解释:
7个区间分别为:(1,4),(1,5),(2,4),(2,5),(3,3),(3,5),(4,5)
Input
第一行一个整数N
第二行N个整数Ai
第三行N个整数Bi
Output
一行,一个整数Ans
Input示例
5
1 4 2 3 4
3 2 2 4 1
Output示例
 
7
 
题解:
  第一种做法是 两个单调栈 + 二分
  两个数都同时维护一个单调递减的 栈
  当元素出栈是我们可以确定以其为最大值所能掌管的一段区间,那么 在对应另一个栈内通过二分找到此值也能掌管一段区间
  求出区间交就可以了
  第二种做法比较类似
  枚举固定最大值为x 的区间有哪些
  在A数组中 假设x 所在位置为  i   ,利用单调栈可以求出 其掌管范围 [L1,R1], 对应的
  在B数组中 假设x 所在位置为  i   ,利用单调栈可以求出 其掌管范围 [L2,R2],
  那么抽出 [L1,i] [i,R1],  [L2,i] [i,R2], 在二维坐标系上就是两个矩阵,求出矩阵面积交就可以了
#include <bits/stdc++.h>
inline long long read(){long long x=0,f=1;char ch=getchar();while(ch<0||ch>9){if(ch==-)f=-1;ch=getchar();}while(ch>=0&&ch<=9){x=x*10+ch-0;ch=getchar();}return x*f;}
using namespace std;
#define ls i<<1
#define rs ls | 1
#define mid ((ll+rr)>>1)
#define MP make_pair
typedef long long LL;
typedef unsigned long long ULL;
const long long INF = 1e18+1LL;
const double pi = acos(-1.0);
const int  N = 4e5 + 10, M = 1e3, inf = 2e9;

int n,a[N],b[N];
int l[2],r[2],q[2][N],pos[2][N];
LL cal(int x,int p,int i) {
     int ll = l[p], rr = r[p],ok = ll;
     while(ll <= rr) {
        if(q[p][mid] > x) {
            ll = mid + 1;
        } else ok = mid,rr = mid - 1;
    }
  //  cout<<i<<" "<<p<<" "<<ok<<" "<<q[p][ok]<<" "<<x<<endl;
    int mmp1,mmp2;
    mmp1 = max(pos[p][ok-1]+1,pos[p^1][r[p^1]-1]+1);
    mmp2 = i-1;

    if(q[p][ok] == x)
        return 1LL * max(min(pos[p^1][r[p^1]],pos[p][ok]) - mmp1+1,0)
        * max(mmp2 - max(pos[p^1][r[p^1]],pos[p][ok]) + 1,0);
    else return 0;
}
int main() {
    cin >> n;
    for(int i = 1; i <= n; ++i) scanf("%d",&a[i]);
    for(int i = 1; i <= n; ++i) scanf("%d",&b[i]);
    l[0] = l[1] = 1;
    r[0] = r[1] = 0;
    pos[0][0] = pos[1][0] = 0;
    LL ans = 0;
    for(int i = 1; i <= n; ++i) {
        LL ret = 0;
        while(l[0] <= r[0] && a[i] >= q[0][r[0]]) {
            ret += cal(q[0][r[0]],1,i);
            r[0]--;
        }
        q[0][++r[0]] = a[i];pos[0][r[0]] = i;
        while(l[1] <= r[1] && b[i] >= q[1][r[1]]) {
            ret += cal(q[1][r[1]],0,i);
            r[1]--;
        }
        q[1][++r[1]] = b[i];pos[1][r[1]] = i;
        //cout<<"fuck "<<i<<" "  << ret<<endl;
        ans += ret;
    }
    while(l[0] <= r[0]) {
        ans += cal(q[0][r[0]],1,n+1);
        r[0]--;
    }
    cout<<ans<<endl;
    return 0;
}

 


51NOD 1962 区间计数 单调栈+二分 / 线段树+扫描线

标签:扫描线   技术   while   1.5   view   比较   style   线段   code   

原文地址:http://www.cnblogs.com/zxhl/p/7596793.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!