51nod 1810 连续区间

感觉跟中位数那题很像啊,不过简单一点还是不会

大力分治,那么要求的就是左端点在左区间,右端点在右区间的满足是一个连续排列的数量

对于一个连续的排列(设i是左端点j是右端点),有max-min+1=j-i+1

那么分情况讨论

枚举其中一个端点,若max,min都在这一边,那么可以计算另一端的端点

否则有两个条件:mx[i]>mx[j]&&mn[i]>mn[j](mx在左mn在右,反过来也是类似的)考虑mx单调增,mn单调减,用两个指针扫描,对于j的延伸,mn一定满足条件,只需要判断mx是否满足。对于i的延伸,mx一定满足条件,只需要判断mn是否满足。

#include<cstdio>
#include<iostream>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<cmath>

#define v(i) v[i+1000010]
using namespace std;
typedef long long LL;

int a[1100000],mx[1100000],mn[1100000];LL ans;
int v[3100000];
void solve(int l,int r)
{
    if(l==r){ans++;return ;}
    int mid=(l+r)/2;
    solve(l,mid),solve(mid+1,r);
    
    mx[mid]=mn[mid]=a[mid];
    for(int i=mid-1;i>=l;i--)
        mx[i]=max(mx[i+1],a[i]), mn[i]=min(mn[i+1],a[i]);
    mx[mid+1]=mn[mid+1]=a[mid+1];
    for(int j=mid+2;j<=r;j++)
        mx[j]=max(mx[j-1],a[j]), mn[j]=min(mn[j-1],a[j]);
        
    for(int i=mid;i>=l;i--)
    {
        int L=mx[i]-mn[i]+1;
        int j=i+L-1;
        if(mid+1<=j&&j<=r&&mx[i]>mx[j]&&mn[i]<mn[j])ans++;
    }
    for(int j=mid+1;j<=r;j++)
    {
        int L=mx[j]-mn[j]+1;
        int i=j-L+1;
        if(l<=i&&i<=mid&&mx[j]>mx[i]&&mn[j]<mn[i])ans++;
    }
    
    int h=mid+1,t=mid+1;//mx在左,mn在右 
    for(int i=mid;i>=l;i--)
    {
        while(t<=r&&mx[i]>mx[t])v(mn[t]+t)++,t++;
        while(h<t&&mn[i]<mn[h])v(mn[h]+h)--,h++;
        ans+=v(mx[i]+i);
    }
    while(h<t)v(mn[h]+h)--,h++;
    
    h=mid,t=mid;
    for(int j=mid+1;j<=r;j++)
    {
        while(t>=l&&mx[j]>mx[t])v(mn[t]-t)++,t--;
        while(h>t&&mn[j]<mn[h])v(mn[h]-h)--,h--;
        ans+=v(mx[j]-j);
    }
    while(h>t)v(mn[h]-h)--,h--;
}
int main()
{
    int n;
    scanf("%d",&n);
    for(int i=1;i<=n;i++)scanf("%d",&a[i]);
    ans=0;solve(1,n);
    printf("%lld\n",ans);
    return 0;
}

 

posted @ 2018-10-19 08:08  AKCqhzdy  阅读(149)  评论(0编辑  收藏  举报