51nod 1810 連續區間
阿新 • • 發佈:2018-10-19
情況 for main 指針 style 中位數 name through 這一
感覺跟中位數那題很像啊,不過簡單一點還是不會
大力分治,那麽要求的就是左端點在左區間,右端點在右區間的滿足是一個連續排列的數量
對於一個連續的排列(設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; }
51nod 1810 連續區間