题意:n*n的一张图,每行每列保证有且仅有一个军队,如果有一个k×k的区域里面恰有k个军队,危险程度就+1,求这张图的危险程度。对于30%的数据,N ≤ 100;对于60%的数据,N ≤ 5000;对于100%的数据,N ≤ 50000
题解:因为每行每列保证有且仅有一个军队,所以可以把整张图“拍扁”,a[x][y]=1-->a[x]=y;n方的复杂度这题抗不住,听说减减枝可以卡到91,之后就卡不动了。。
正解是分治或线段树,我打的是分治。
对于一个区间,可以对答案做贡献的条件就是序列是连续的(但顺序可以是乱的),即max-min=k-1。这个区间对答案的贡献可以分为3部分,mid左边,mid右边,横跨mid。mid左右的直接分治就行,关键就在与横跨mid的那部分。
横跨mid又可以分为4中情况max min都在mid左,max min都在mid右,min在mid左max在mid右,max在mid左min在mid右。发现1 2,3 4是对称的,所以只说一种。
max min都在mid左:lmax[l]-lmin[l]+1=r-l+1 发现枚举左端点就可以得到右端点,判断一下右端点是否合法就可以了;
min在mid左max在mid右:rmax[R]-lmin[L]=R-L 移项得rmax[R]-R=lmin[L]-L 用桶存符合情况rmax[R]-R,用左边拿出来就行;判是否符合情况的思想有点类似与单调队列的思想,rmax等数组存的是前缀max或min,具有单调性。注意判R的时候只能用min卡,因为如果rmax[R]>lmax[L],那么a[R]<lmax[l]这种情况在rmax中没有体现,但他却是不合法的;用rmin[R]>lmin[l]的话,一旦a[R]<lmin[l],rmin[R]中也会体现,可以卡住。L同理。
枚举左端点,越往右走lmin越大,lmax越小,就要求R的rmin增大已使继续满足lmin<rmin 所以让R左移同时排除不符合的情况,而对L的rmax的限制就有所放宽,可以稍小一些,所以L也左移,同时把符合的放进去。最后用ans+=t[lmin[L]-L]。值得注意的是,桶里的个数可能是负的,即没有符合的情况,所以加ans的时候需要判一下是否>0。而负的对以后更新没有任何影响,不用管他就行了。
桶的下标可能会出负数,统一+n就可以避免这种情况了。
清空桶的时候不要用memset,因为太慢了,还是手清吧。。
再一个卡我的地方就是进出桶的时候判断下一个是否合法,所以范围应该是开区间,最多到倒数第二个,不然倒数第一个怎么判
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> using namespace std; int n,a[50010],lmax[50010],lmin[50010],rmax[50010],rmin[50010],t[100010]; int search(int l,int r,int mid) { int ans=0; lmax[mid]=lmin[mid]=a[mid]; rmax[mid+1]=rmin[mid+1]=a[mid+1]; for(int i=mid-1;i>=l;i--){ lmax[i]=max(lmax[i+1],a[i]); lmin[i]=min(lmin[i+1],a[i]); } for(int i=mid+2;i<=r;i++){ rmax[i]=max(rmax[i-1],a[i]); rmin[i]=min(rmin[i-1],a[i]); } for(int i=l;i<=mid;i++){ int j=lmax[i]-lmin[i]+i; if(j<=r&&j>=mid+1&&lmin[i]<rmin[j]&&lmax[i]>rmax[j]) ans++; } for(int i=mid+1;i<=r;i++){ int j=i-rmax[i]+rmin[i]; if(j>=l&&j<=mid&&lmin[j]>rmin[i]&&lmax[j]<rmax[i]) ans++; } int L=mid+1,R=mid+1; while(R<=r&&rmin[R]>lmin[l]){ t[rmax[R]-R+n]++; R++; } while(L<=r&&rmax[L]<lmax[l]){ t[rmax[L]-L+n]--; L++; } for(int i=l;i<=mid;i++){ while(R>mid+1&&rmin[R-1]<lmin[i]){ R--; t[rmax[R]-R+n]--; } while(L>mid+1&&rmax[L-1]>lmax[i]){ L--; t[rmax[L]-L+n]++; } if(t[lmin[i]-i+n]>0) ans+=t[lmin[i]-i+n]; } for(int i=mid+1;i<=r;i++) t[rmax[i]-i+n]=0; L=mid,R=mid; while(L>=l&&lmin[L]>rmin[r]){ t[lmax[L]+L]++; L--; } while(R>=l&&lmax[R]<rmax[r]){ t[lmax[R]+R]--; R--; } for(int i=r;i>=mid+1;i--){ while(L<mid&&lmin[L+1]<rmin[i]){ L++; t[lmax[L]+L]--; } while(R<mid&&lmax[R+1]>rmax[i]){ R++; t[lmax[R]+R]++; } if(t[rmin[i]+i]>0) ans+=t[rmin[i]+i]; } for(int i=l;i<=mid;i++) t[i+lmax[i]]=0; return ans; } int work(int l,int r) { if(l==r) return 1; int mid=(l+r)>>1; return work(l,mid)+work(mid+1,r)+search(l,r,mid); } int main() { scanf("%d",&n); int u,v; for(int i=1;i<=n;i++){ scanf("%d%d",&u,&v); a[u]=v; } printf("%d\n",work(1,n)); }