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; }
pain and happy in the cruel world.