1 #include<cstdio> 2 #include<algorithm> 3 using namespace std; 4 const int maxn=50000+10; 5 int a[maxn],Min[maxn],Max[maxn],cnt[maxn<<1]; 6 int ans=0,n; 7 void Divi(int l,int r){ 8 if(l==r){ 9 ans++; 10 return; 11 }//当只有一个点时,一定为一个完美子图,ans++ 12 int mid=(l+r)>>1; 13 Divi(l,mid);//递归左边 14 Divi(mid+1,r);//递归右边 15 Min[mid]=Max[mid]=a[mid]; 16 Min[mid+1]=Max[mid+1]=a[mid+1]; 17 for(int i=mid-1;i>=l;i--){ 18 Min[i]=min(Min[i+1],a[i]); 19 Max[i]=max(Max[i+1],a[i]); 20 } 21 for(int i=mid+2;i<=r;i++){ 22 Min[i]=min(Min[i-1],a[i]); 23 Max[i]=max(Max[i-1],a[i]); 24 }//初始化,Max,Min都是对于mid而言 25 for(int i=mid;i>=l;i--){ 26 int j=i+Max[i]-Min[i]; 27 if(j<=r&&j>mid&&Max[j]<Max[i]&&Min[i]<Min[j]) ans++; 28 }//最大最小值都在左端,判断时保证j在右端 29 for(int j=mid+1;j<=r;j++){ 30 int i=j-Max[j]+Min[j]; 31 if(i>=l&&i<=mid&&Max[i]<Max[j]&&Min[i]>Min[j]) ans++; 32 }//最大最小值都在右端,判断时i在左端 33 int j=mid+1,k=mid+1; 34 for(int i=mid;i>=l;i--){//左小右大 35 while(j<=r&&Min[j]>Min[i]){//j满足左小 36 cnt[Max[j]-j+n]++; 37 j++; 38 }//因为Max[j]-j可能为负数,所以加n 39 //不改变j的值,因为第一个while中若新的i的Min[i]不变,一定会走到当前的j,若更小,一定会走到当前的j,且可能向后走 40 while(k<j&&Max[i]>Max[k]){//若不满足右大,cnt--,因为若左大左小,前面已经计算过 41 cnt[Max[k]-k+n]--; 42 k++; 43 }//不改变k的值,因为第二个while中若新的i的Max[i]不变,一定会走到当前的k,若更大,一定会走到当前的k,且可能向后走 44 ans+=cnt[Min[i]-i+n];//因为Max[j]-j==Min[i]-i 45 } 46 while(k<j){ 47 cnt[Max[k]-k+n]--; 48 k++; 49 }//不用memset,会T掉 50 j=mid,k=mid; 51 for(int i=mid+1;i<=r;i++){ 52 while(j>=l&&Min[i]<Min[j]){ 53 cnt[Max[j]+j]++; 54 j--; 55 } 56 while(k>j&&Max[k]<Max[i]){ 57 cnt[Max[k]+k]--; 58 k--; 59 } 60 ans+=cnt[Min[i]+i]; 61 } 62 while(k>j){ 63 cnt[Max[k]+k]--; 64 k--; 65 } 66 } 67 int main(){ 68 scanf("%d",&n); 69 for(int i=1;i<=n;i++){ 70 int x,y; 71 scanf("%d%d",&x,&y); 72 a[x]=y; 73 } 74 Divi(1,n); 75 printf("%d",ans); 76 return 0; 77 }
原文:https://www.cnblogs.com/HZOIDJ123/p/13285477.html