3.奇袭
(raid.cpp\c\pas)
【问题描述】
由于各种原因,桐人现在被困在Under World(以下简称UW)中,而UW马上要迎来最终的压力测试——魔界入侵。
唯一一个神一般存在的Administrator被消灭了,靠原本的整合骑士的力量是远远不够的。所以爱丽丝动员了UW全体人民,与整合骑士一起抗击魔族。
在UW的驻地可以隐约看见魔族军队的大本营。整合骑士们打算在魔族入侵前发动一次奇袭,袭击魔族大本营!
为了降低风险,爱丽丝找到了你,一名优秀斥候,希望你能在奇袭前对魔族大本营进行侦查,并计算出袭击的难度。
经过侦查,你绘制出了魔族大本营的地图,然后发现,魔族大本营是一个N ×N的网格图,一共有N支军队驻扎在一些网格中(不会有两只军队驻扎在一起)。
在大本营中,每有一个k×k(1≤k≤N)的子网格图包含恰好k支军队,我们袭击的难度就会增加1点。
现在请你根据绘制出的地图,告诉爱丽丝这次的袭击行动难度有多大。
【输入格式】
第一行,一个正整数N,表示网格图的大小以及军队数量。接下来N行,每行两个整数,Xi,Yi,表示第i支军队的坐标。
保证每一行和每一列都恰有一只军队,即每一个Xi和每一个Yi都是不一样
的。
【输出格式】
一行,一个整数表示袭击的难度。
【输入输出样例】
raid.in
5
1 1
3 2
2 4
5 5
4 3
raid.out
10
【样例解释】
显然,分别以(2,2)和(4,4)为左上,右下顶点的一个子网格图中有3支军队,这为我们的难度贡献了1点。
类似的子网格图在原图中能找出10个。【数据范围】
对于30%的数据,N ≤ 100
对于60%的数据,N ≤ 5000 对于100%的数据,N ≤ 50000
题目的关键点:
1)每一行和每一列都恰有一只军队。因此合法矩形只要满足 存在军队的点的横坐标的最值之差 = 存在军队的点的纵坐标最值之差 即可。
换言之,若以x作为y的下标,即存在军队的点(x,y)等价于(x,map[x]),则合法矩形满足 max(map[x])-min(map[x]) = right - left
其中 x∈(left,right)
2)因为 max(map[x])-min(map[x]) = right - left = 边长d,所以当最值之差确定,则边长确定。
3)n的范围决定了AC解法不能是O(n²)的,因此无法同时枚举起点和矩形边长,由此排除dp
......我知道该题真的很像dp(○´?д?)?
对于纵坐标在(l,r)之间的地图,合法矩形的数目 = 只覆盖左侧的矩形 + 只覆盖右侧的矩形 + 同时覆盖左右的矩形
前两者用递归统计答案,因此每一个区间只用统计同时覆盖左右的矩形即可
同时覆盖左右的矩形,它的区间最值分布可能是:
- 同在矩形左侧
- 最小值在矩形左侧,最大值在矩形右侧
- 同在矩形右侧
- 最小值在矩形右侧,最大值在矩形左侧
在地图(l,r)枚举矩形的边界i
由于矩形是连续的而不能割裂,因此维护最值是矩形的边界到中点的最值
如max[i]维护的是区间[i,mid]的最大值(l<=i<=mid)
max[i]维护的是区间[mid+1,i]的最大值(mid<i<=r)
1)同在矩形左侧
设左边界为i
矩形边长d = max[i,mid] - min[i,mid]
矩形右边界 j = i + d = i + max[i,mid] - min[i,mid]
- ∵同时覆盖左右 ∴j >= mid 且 j <= r
- ∵最小值在左侧 ∴min(mid,j] > min[i,mid]
- ∵最大值也在左侧 ∴max(mid,i] > max[j,mid]
2)最小值在矩形左侧,最大值在矩形右侧
max(map[mid,x])-min(map[x,mid]) = right - left
移项可得 max(map[mid,x])-right = min(map[x,mid]) - left
因为此处枚举的是左边界i,因此 min(map[x,mid])-left = min(map[i,mid])- i 只与当前i有关
- ∵同时覆盖左右 ∴j >= mid 且 j <= r
- ∵最小值在左侧 ∴min(mid,j] > min[i,mid]
- ∵最大值在右侧 ∴max(mid,i] < max[j,mid]
而 max(map[mid,x]) 递增,min(map[mid,x])递减
因此可以用桶排序筛选合法的右边界
int m=(l+r)/2; mmax[m]=mmin[m]=map[m]; mmax[m+1]=mmin[m+1]=map[m+1]; /* mmin[i]指i到mid这段区间的最值 */ for(int i=m-1;i>=l;i--) {mmin[i]=min(mmin[i+1],map[i]); mmax[i]=max(mmax[i+1],map[i]);} for(int i=m+2;i<=r;i++) {mmin[i]=min(mmin[i-1],map[i]); mmax[i]=max(mmax[i-1],map[i]);} int z1=m+1,z2=m+1; for(int i=m;i>=l;i--) { int j=mmax[i]-mmin[i]+i; /* 最值同在左侧 */ if(j>m&&j<=r&&mmax[j]<mmax[i]&&mmin[j]>mmin[i]) ans++; /* 最大值在右侧 且满足最小值在左侧 */ /* 当mmax[k1]-k1=mmin[k2]-k2时矩形(k2,mmin[k2],k1,mmax[k1])合法 */ while(z2<=r&&mmin[z2]>=mmin[i]) {sum[mmax[z2]-z2+MAX]++;z2++;} while(z1<=r&&mmax[z1]<=mmax[i]) {sum[mmax[z1]-z1+MAX]--;z1++;} ans+=max(sum[mmin[i]-i+MAX],0); }
右边界的处理同理
z1=m;z2=m; for(int i=l;i<=r;i++) sum[mmax[i]-i+MAX]=0; for(int i=m+1;i<=r;i++) { int j=i-mmax[i]+mmin[i]; if(j>=l&&j<=m&&mmax[j]<mmax[i]&&mmin[j]>mmin[i]) ans++; while(z2>=l&&mmin[z2]>=mmin[i]) {sum[mmax[z2]+z2+MAX]++;z2--;} while(z1>=l&&mmax[z1]<=mmax[i]) {sum[mmax[z1]+z1+MAX]--;z1--;} ans+=max(sum[mmin[i]+i+MAX],0); } for(int i=l;i<=r;i++) sum[mmax[i]+i+MAX]=0;
完整的AC代码
#include<cstdio> #include<algorithm> using namespace std; const int MAX=50000; int n; int map[50001],mmax[50001],mmin[50001]; int sum[50001+MAX]; int ans=0; void work(int l,int r) { if(l==r) return; int m=(l+r)/2; mmax[m]=mmin[m]=map[m]; mmax[m+1]=mmin[m+1]=map[m+1]; /* mmin[i]指i到mid这段区间的最值 */ for(int i=m-1;i>=l;i--) {mmin[i]=min(mmin[i+1],map[i]); mmax[i]=max(mmax[i+1],map[i]);} for(int i=m+2;i<=r;i++) {mmin[i]=min(mmin[i-1],map[i]); mmax[i]=max(mmax[i-1],map[i]);} int z1=m+1,z2=m+1; for(int i=m;i>=l;i--) { int j=mmax[i]-mmin[i]+i; /* 最值同在左侧 */ if(j>m&&j<=r&&mmax[j]<mmax[i]&&mmin[j]>mmin[i]) ans++; /* 最大值在右侧 且满足最小值在左侧 */ /* 前缀和基于mmax[k1]-k1=mmin[k2]-k2时矩形(k2,mmin[k2],k1,mmax[k1])合法 */ while(z2<=r&&mmin[z2]>=mmin[i]) {sum[mmax[z2]-z2+MAX]++;z2++;} while(z1<=r&&mmax[z1]<=mmax[i]) {sum[mmax[z1]-z1+MAX]--;z1++;} ans+=max(sum[mmin[i]-i+MAX],0); } z1=m;z2=m; for(int i=l;i<=r;i++) sum[mmax[i]-i+MAX]=0; for(int i=m+1;i<=r;i++) { int j=i-mmax[i]+mmin[i]; if(j>=l&&j<=m&&mmax[j]<mmax[i]&&mmin[j]>mmin[i]) ans++; while(z2>=l&&mmin[z2]>=mmin[i]) {sum[mmax[z2]+z2+MAX]++;z2--;} while(z1>=l&&mmax[z1]<=mmax[i]) {sum[mmax[z1]+z1+MAX]--;z1--;} ans+=max(sum[mmin[i]+i+MAX],0); } for(int i=l;i<=r;i++) sum[mmax[i]+i+MAX]=0; work(l,m); work(m+1,r); } int main() { scanf("%d",&n); for(int i=1;i<=n;i++) { int a,b;t scanf("%d%d",&a,&b); map[a]=b; } work(1,n); printf("%d",ans+n); }
HYX说可以用 分块+单调栈 做,不过还没有看分块的我(○´?д?)?
那么看了分块以后就回来写这道题吧咕咕咕
原文地址:https://www.cnblogs.com/orange-/p/10801533.html