51nod 1962区间计数(单调栈加二分)

题目要求是求出两个序列中处于相同位置区间并且最大值相同的区间个数,我们最直观的感受就是求出每个区间的最大值,这个可以O(N)的求,利用单调栈求出每个数作为最大值能够覆盖的区间。

然后我们可以在进行单调栈的时候统计一下答案,怎么统计呢?就是在一个数列弹栈的时候在另一个数列的单调栈中找到这个数,然后分别算出两个数列中所对应的区间,然后统计一下左端点和右端点能够取到的所有位置利用乘法原理求一下即可。—— by VANE

#include<bits/stdc++.h>
using namespace std;
const int N=4e5+5;
int n;
typedef long long ll;
ll ans;
int a[N][2],q[N][2],s[2];
ll calc(int pos,int v,int l,int r,int k)
{
    int L=1,R=s[k],mid,ans=1e9;
    while(L<=R)
    {
        mid=L+R>>1;
        if(a[q[mid][k]][k]==v) {ans=mid;break;}
        if(a[q[mid][k]][k]>v) L=mid+1;
        else R=mid-1;
    }
    if(ans==1e9) return 0;
    return 1ll*max(min(pos,q[ans][k])-max(l,q[ans-1][k]+1)+1,0)*(r-max(pos,q[ans][k])+1);
}
int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;++i) scanf("%d",&a[i][0]);
    for(int i=1;i<=n;++i) scanf("%d",&a[i][1]);
    s[1]=s[0]=q[1][0]=q[1][1]=1;
    for(int i=2;i<=n;++i)
        for(int j=0;j<2;++j)
        {
            while(s[j]&&a[q[s[j]][j]][j]<=a[i][j]) ans+=calc(q[s[j]][j],a[q[s[j]][j]][j],q[s[j]-1][j]+1,i-1,j^1),s[j]--;
            q[++s[j]][j]=i;
        }
    for(int i=1;i<=s[1];i++) ans+=calc(q[i][1],a[q[i][1]][1],q[i-1][1]+1,n,0);
    printf("%lld\n",ans);
}

 

posted @ 2017-12-20 19:36  大奕哥&VANE  阅读(129)  评论(0编辑  收藏  举报