7.16T3

保证每一行和每一列都恰有一只军队,即每一个Xi和每一个Yi都是不一样的。

这是道毒瘤题,毒瘤的不能再毒瘤,你可以想出各种$O(n^5)$,$O(n^4)$,$O(n^3)$以至于$O(n^2)$的算法,然而你都会T,且卡常卡不过去,而正解是个$O(nlogn)$的

关于超时的算法我其实还是想说一说,由于考试的时候我zz,所以最好也就打了一个$n^4$,实际上这题的$n^3$很好打,就是枚举起始点以及块的大小,这个复杂度最多也就T27吧

然后是$n^2$的,那么你就需要认真读题,我没好好读题,我不是人,注意一下我留下的标红加粗那一行(为什么考试的时候没人给我加粗。。。。。),他说每行每列就一个,那我们把这个方格图拍扁怎么样,我们把它从二维搞成一维,也就是表示成$a[x]=y$的形式,那么思考一下,对于一个区间$[L,R]$,这个$LR$就是横坐标的范围,也就是子方格图的宽,而$a[L]$到$a[R]$之间的$max$和$min$就是纵坐标的范围,也就是子方格图的长,那只要$max-min=R-L$,这个区间就是一个合法的区间,这样的话两层循环枚举$L$和$R$就达到了$n^2$,就可以愉快的T67了,据某些Dalao说$n^2$卡一卡可以T91

接下来就是正解了,需要用到刚才拍扁方格的思路,还要用的二分,单调性以及桶

如果我们用二分的想法的话,$ans[L,R]=ans[L,mid]+ans[mid+1,R]+$跨过$mid$部分的合法方案,关于前两项,递归下去去算就没问题,重点需要解决的是第三项,我们来想一想这个跨过$mid$都包含些什么情况

1.$min$ $max$都在左边

2.$min$ $max$都在右边

3.$min$在左边$max$在右边

4.$min$在右边$max$在左边

我们化减一下这四种方案变成  一.最值在同侧  二.最值在异侧

首先我们需要记录一下从$mid$到$L$中间这些区间内的最值以及$mid+1$到$R$中间这些区间的最值

一.最值在同侧(以均在左侧为例)

如果最值都在左侧,那么就需要满足$max[l]-min[l]=r-l$,这个和$n^2$的思路是一样的,我们移一下项,就得到了$r=max[l]-min[l]+l$,那我们就可以枚举这个$l$,来确定每一个对应的r,来看是不是符合条件

如果都在右侧,同样的思路枚举右端点就可以了

二.最值在异侧(以最小值在左最大值在右为例)

我在说正解之前提到了单调性,那什么具有单调性呢?这个区间$max$ $min$具有单调性,因为左区间的最值都是有$mid$开始推的,那从$mid$到$L$,$min$严格不上升,$max$严格不下降,这个很显然嘛,那同理,从$mid+1$到$R$ $min$严格不上升,$max$严格不下降,我们假设左区间是$[L,mid]$,那什么样的右区间是合法的呢?我们设两个指针$r1$,$r2$,把他们的初始位置放在$mid+1$处,我们在移动指针之前先想一想右区间应该满足什么才可以称作合法,诶呀,不就是$min[r]>min[l]$ $max[r]>max[l]$嘛,关于这个$max$我们可以找到第一个比$max[l]$大的,由于这个单调性,第一个$max$及其之后都大于$max[l]$,我们还可以找到最后一个比$min[l]$大的,同样由于这个单调性最后一个之后的$min$都小于$min[l]$,变成了不合法区间,这样的话我们用$r1$,$r2$这两个指针找到这个第一个和最后一个,这中间就是合法方案,他还需要满足一个什么条件来着?$max[r]-min[l]=r-l$,还是移项,那就可以变形为$max[r]-r=min[l]-l$,这样的话等式两边就变成了两个独立的部分,我们把这个独立的部分扔进桶里,让他进行加加减减的操作我们就可以得到正确的区间数,当然了,我们最开始定下了大左区间$[L,mid]$,我们现在需要缩小它,那$r1$和$r2$两个指针也就接着移动就可以了

如果最大值在左,最小值在右,也是一样的思路,在左边找到合法区间,移动右区间就可以了

当然这题还可以翻转,就是那个$reverse$,代码会变短,复杂度的话多个$logn$

注意:桶记得清空,$min[l]-l$可能小于0,所以再加个$n$

 1 #include<iostream>
 2 #include<cstdio>
 3 #define maxn 50010
 4 #define ll long long
 5 using namespace std;
 6 int n,maxx,minn;
 7 ll ans;
 8 int a[maxn],mi[maxn],ma[maxn],tong[maxn*3];
 9 inline int read()
10 {
11     int e=0,f=1;  char ch=getchar();
12     while(ch<'0'||ch>'9')
13     {
14         if(ch=='-')  f=-1;
15         ch=getchar();
16     }
17     while(ch>='0'&&ch<='9')  {e=(e<<3)+(e<<1)+(ch-48);  ch=getchar();}
18     return e*f;
19 }
20 ll fz(int l,int r)
21 {
22     if(l==r)  return 1;
23     ll da=0,sum=0;  int mid=(l+r)>>1;
24     da=fz(l,mid)+fz(mid+1,r);
25     ma[mid]=mi[mid]=a[mid];
26     for(int i=mid-1;i>=l;--i)  {ma[i]=max(ma[i+1],a[i]);  mi[i]=min(mi[i+1],a[i]);}
27     ma[mid+1]=mi[mid+1]=a[mid+1];
28     for(int i=mid+2;i<=r;++i)  {ma[i]=max(ma[i-1],a[i]);  mi[i]=min(mi[i-1],a[i]);}
29     for(int i=mid;i>=l;--i)//都在左侧
30     {
31         int R=ma[i]-mi[i]+i;
32         if(R<=mid||R>r)  continue;
33         if(ma[R]<ma[i]&&mi[R]>mi[i])  sum++;
34     }
35     for(int i=mid+1;i<=r;++i)//都在右侧
36     {
37         int L=i-ma[i]+mi[i];
38         if(L>=mid+1||L<l)  continue;
39         if(ma[L]<ma[i]&&mi[L]>mi[i])  sum++;
40     }
41     int r1=mid+1,r2=mid+1;//最小值在左,最大值在右
42     while(r1<=r&&mi[r1]>mi[l])  {tong[n+ma[r1]-r1]++;  r1++;}
43     while(r2<=r&&ma[r2]<ma[l])  {tong[n+ma[r2]-r2]--;  r2++;}
44     for(int L=l;L<=mid;++L)
45     {
46         while(r1>mid+1&&mi[r1-1]<mi[L])  {r1--;  tong[n+ma[r1]-r1]--;}
47         while(r2>mid+1&&ma[r2-1]>ma[L])  {r2--;  tong[n+ma[r2]-r2]++;}        
48         if(tong[n+mi[L]-L]>0)  sum+=tong[n+mi[L]-L];
49     }
50     for(int i=mid+1;i<=r;++i)  tong[n+ma[i]-i]=0;
51     r1=mid;  r2=mid;//最大值在坐,最小值在右
52     while(r1>=l&&mi[r1]>mi[r])  {tong[n+ma[r1]+r1]++;  r1--;}
53     while(r2>=l&&ma[r2]<ma[r])  {tong[n+ma[r2]+r2]--;  r2--;}
54     for(int R=r;R>=mid+1;--R)
55     {
56         while(r1<mid&&mi[r1+1]<mi[R])  {r1++;  tong[n+ma[r1]+r1]--;}
57         while(r2<mid&&ma[r2+1]>ma[R])  {r2++;  tong[n+ma[r2]+r2]++;}
58         if(tong[n+mi[R]+R]>0)  sum+=tong[n+mi[R]+R];
59     }
60     for(int i=l;i<=mid;++i)  tong[n+ma[i]+i]=0;
61     da+=sum;
62     //cout<<"from"<<l<<"to"<<r<<"sum="<<sum<<endl;
63     return da;
64 }
65 int main()
66 {
67     n=read();
68     for(int i=1;i<=n;++i)  {int o=read(),p=read();  a[o]=p;}
69     ans=fz(1,n);
70     printf("%lld\n",ans);
71     return 0;
72 }
View Code

 

posted @ 2019-07-17 17:53  hzoi_X&R  阅读(196)  评论(0编辑  收藏  举报