奇袭
时间限制: 1 Sec 内存限制: 256 MB
题目描述
由于各种原因,桐人现在被困在Under World(以下简称UW)中,而UW马上 要迎来最终的压力测试——魔界入侵。
唯一一个神一般存在的Administrator被消灭了,靠原本的整合骑士的力量 是远远不够的。所以爱丽丝动员了UW全体人民,与整合骑士一起抗击魔族。
在UW的驻地可以隐约看见魔族军队的大本营。整合骑士们打算在魔族入侵前 发动一次奇袭,袭击魔族大本营!
为了降低风险,爱丽丝找到了你,一名优秀斥候,希望你能在奇袭前对魔族 大本营进行侦查,并计算出袭击的难度。
经过侦查,你绘制出了魔族大本营的地图,然后发现,魔族大本营是一个N ×N的网格图,一共有N支军队驻扎在一些网格中(不会有两只军队驻扎在一起)。
在大本营中,每有一个k×k(1≤k≤N)的子网格图包含恰好k支军队,我们袭 击的难度就会增加1点。
现在请你根据绘制出的地图,告诉爱丽丝这次的袭击行动难度有多大。
输入
第一行,一个正整数N,表示网格图的大小以及军队数量。
接下来N行,每行两个整数,Xi,Yi,表示第i支军队的坐标。
保证每一行和每一列都恰有一只军队,即每一个Xi和每一个Yi都是不一样的。
输出
样例输入
5
1 1
3 2
2 4
5 5
4 3
样例输出
10
提示
【样例解释】
显然,分别以(2,2)和(4,4)为左上,右下顶点的一个子网格图中有3支军队,
这为我们的难度贡献了1点。
类似的子网格图在原图中能找出10个。
【数据范围】
对于30%的数据,N ≤ 100
对于60%的数据,N ≤ 5000
对于100%的数据,N ≤ 50000
题解
考试的时候理解题意不够清楚,甚至都没注意到每行每列都只有一个,居然还一样骗了27的暴力分……但是如果看到的话应该不至于连64分的n^2暴力都想不到吧。数组显然是开不了二维的,直接用a[i]=j表示第i行j列有军队。这样就把矩阵上的问题转化到了序列里,看一个区间里面max(a[i])-min(a[i])=r-l是否满足,满足则ans++。但是这样统计很慢,需要分治。solve(l,r)求l到r区间里答案的个数,左区间的答案和右区间的答案是可以再向下分的,那么需要在这个函数里求的就是跨中线的答案了。
定义:mal[i]、mil[i]:在[i,mid]区间内的最大、最小a;mar[i]、mir[i]:在[mid+1,i]区间内的最大、最小a。
按区间最大最小值的位置分四种情况:
max、min都在左边:从mid向l枚举i,如果区间[i,j]是一个合法解则应满足mal[i]-mil[i]=j-i,mar[j]<mal[i],mid<j<=r,mir[j]>mil[i]。找到合法解就把ans++。
max、min都在右边同理。
min在左边,max在右边:从mid向l枚举i,如果区间[i,j]是一个合法解则应满足mar[j]-mil[i]=j-i,移项得mar[j]-j=mil[i]-i,这样我们只需要统计从mid+1到r所有的mar[j]-j对应的可行解个数就可以通过mil[i]-i来得到i对应的解了,这可以用桶实现。mar当然是单调递增的,而mir是单调递减了,用两个指针f、t从mid+1向r移动,当mar[f]<mal[i]时右移f且tong[mar[f]-f]--,当mir[t]>mil[i]时右移t且tong[mar[t]-t]++,容易看出f经过的都是不可行的点而t经过的都是可行的点,对于每一个i移动两个指针完毕后ans+=tong[mil[i]-i]。
max在左边,min在右边同理。
很久没有做过这样的分治题,桶的应用也很新奇。对于一道题目来说,第一步的转化是最基础也是最关键的一步。每一次考试无论成绩如何,对自己的要求至少应该是“把应该想到的东西想清楚”,这大概不算高吧。
#include<iostream> #include<cstdio> #include<cstring> using namespace std; const int sj=50005; const int xf=50000; int n,a[sj],mal[sj],mil[sj],mar[sj],mir[sj],tp,t[sj<<2],z,z1; int bj(int x,int y) { return x>y?x:y; } int xj(int x,int y) { return x<y?x:y; } int solve(int l,int r) { if(l==r) return 1; int mid=(l+r)>>1,ans=solve(l,mid)+solve(mid+1,r); mal[mid]=mil[mid]=a[mid]; mar[mid+1]=mir[mid+1]=a[mid+1]; for(int i=mid-1;i>=l;i--) mal[i]=bj(a[i],mal[i+1]),mil[i]=xj(a[i],mil[i+1]); for(int i=mid+2;i<=r;i++) mar[i]=bj(a[i],mar[i-1]),mir[i]=xj(a[i],mir[i-1]); for(int i=mid;i>=l;i--) { tp=mal[i]-mil[i]+i; if(tp>mid&&tp<=r&&mar[tp]<mal[i]&&mir[tp]>mil[i]) ans++; }//both left for(int i=mid+1;i<=r;i++) { tp=i-(mar[i]-mir[i]); if(tp<=mid&&tp>=l&&mal[tp]<mar[i]&&mil[tp]>mir[i]) ans++; }//both right z=z1=mid+1; for(int i=mid;i>=l;i--) { for(z;z<=r&&mal[i]>mar[z];z++) t[mar[z]-z+xf]--; for(z1;z1<=r&&mir[z1]>mil[i];z1++) t[mar[z1]-z1+xf]++; if(t[mil[i]-i+xf]>0) ans+=t[mil[i]-i+xf]; }//min left max right for(int i=mid+1;i<=r;i++) t[mar[i]-i+xf]=0; z=z1=mid+1; for(int i=mid;i>=l;i--) { for(z;z<=r&&mal[i]>mar[z];z++) t[mir[z]+z+xf]++; for(z1;z1<=r&&mir[z1]>mil[i];z1++) t[mir[z1]+z1+xf]--; if(t[mal[i]+i+xf]>0) ans+=t[mal[i]+i+xf]; }//max left min right for(int i=mid+1;i<=r;i++) t[mir[i]+i+xf]=0; return ans; } int main() { scanf("%d",&n); for(int i=1,j;i<=n;i++) { scanf("%d",&j); scanf("%d",&a[j]); } printf("%d",solve(1,n)); return 0; }