【BZOJ-1127】KUP 悬线法 + 贪心
1127: [POI2008]KUP
Time Limit: 10 Sec Memory Limit: 162 MBSec Special JudgeSubmit: 317 Solved: 111
[Submit][Status][Discuss]
Description
给一个n*n的地图,每个格子有一个价格,找一个矩形区域,使其价格总和位于[k,2k]
Input
输入k n(n<2000)和一个n*n的地图
Output
输出矩形的左上和右下的列-行坐标或NIE
Sample Input
inputdata1
4 3
1 1 1
1 9 1
1 1 1
inputdata2
8 4
1 2 1 3
25 1 2 1
4 20 3 3
3 30 12 2
4 3
1 1 1
1 9 1
1 1 1
inputdata2
8 4
1 2 1 3
25 1 2 1
4 20 3 3
3 30 12 2
Sample Output
outputdata1
NIE
outputdata2
2 1 4 2
NIE
outputdata2
2 1 4 2
HINT
1<=k<=10^9 每个价格都是不大于2*10^9的非负整数
Source
Solution
这个题需要思路....
首先假设有一个一维的区间$[l,r]$,那么假设这个区间中满足$\forall x,x<K$,那么且这个区间的和$>=K$,那么答案肯定存在在这个区间中。
证明:
因为这个区间中满足$\forall x,x<K$,所以区间和每加上一个数,区间和的变化量一定是$<K$的;
所以,并不会存在一个数使得一个子区间(连续的)和加上他得到的新区间和直接从$(-\infty,K]$跳过$[K,2*K]$变成$[2*K,+\infty)$.
所以,只要从这个一维的区间的左/右端开始一一删除,就可以得到满足条件的区间。
但是这里的$N*N$的矩阵,所以要利用这个结论就必须扩展到多维区间块上面,但是这个结论是可以拓展的。
这样就是一个子矩阵满足$\forall x,x<K$,且子矩阵和$>=K$,那么这个子矩阵中存在答案。
证明类比上面的证明,这里分类讨论一下,可以利用上面的方法,先一行一行的删除,删成满足条件的,或者只剩一行,转成上述,再一个一个删.
然后就是找出这些需要搞得子矩形的方法了,把$x>2*K$的点认为是障碍,做一遍悬线法,就可以得到所有的极大子矩形,然后一一判断。
当然一开始读入的时候,如果存在一个$1*1$的位置$x$直接满足$x \in [K,2*K]$那么可以直接输出。
Code
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #include<cmath> using namespace std; #define LL long long inline int read() { int x=0,f=1; char ch=getchar(); while (ch<'0' || ch>'9') {if (ch=='-') f=-1; ch=getchar();} while (ch>='0' && ch<='9') {x=x*10+ch-'0'; ch=getchar();} return x*f; } int K,N,l[2010][2010],r[2010][2010],h[2010][2010]; LL sum[2010][2010],a[2010][2010]; inline LL Sum(int x1,int y1,int x2,int y2) {return sum[x2][y2]+sum[x1-1][y1-1]-sum[x2][y1-1]-sum[x1-1][y2];} inline void Cut(int x1,int y1,int x2,int y2) { while (Sum(x1,y1,x2,y2)>2*K) { if (x1==x2) {y2--; continue;} if (Sum(x1,y1,x2-1,y2)>=K) {x2--; continue;} if (Sum(x1+1,y1,x2,y2)>=K) {x1++; continue;} } printf("%d %d %d %d\n",y1,x1,y2,x2); exit(0); } int main() { K=read(),N=read(); for (int i=1; i<=N; i++) for (int j=1; j<=N; j++) a[i][j]=read(),sum[i][j]=sum[i-1][j]+sum[i][j-1]-sum[i-1][j-1]+a[i][j]; for (int i=1; i<=N; i++) for (int j=1; j<=N; j++) if (a[i][j]>=K && a[i][j]<=2*K) {printf("%d %d %d %d\n",j,i,j,i); return 0;} for (int i=1; i<=N; i++) { for (int j=1,x=0; j<=N; j++) if (a[i][j]<=2*K) l[i][j]=x; else l[i][j]=0,x=j; for (int j=N,x=N+1; j>=1; j--) if (a[i][j]<=2*K) r[i][j]=x; else r[i][j]=N+1,x=j; } for (int i=1; i<=N+1; i++) r[0][i]=N+1; for (int i=1; i<=N; i++) for (int j=1; j<=N; j++) if (a[i][j]<=2*K) h[i][j]=h[i-1][j]+1, l[i][j]=max(l[i][j]+1,l[i-1][j]), r[i][j]=min(r[i][j]-1,r[i-1][j]); // puts("=========================="); // for (int i=1; i<=N; i++,puts("")) // for (int j=1; j<=N; j++) // printf("%d ",h[i][j]); // puts("=========================="); // for (int i=1; i<=N; i++,puts("")) // for (int j=1; j<=N; j++) // printf("%d ",l[i][j]); // puts("=========================="); // for (int i=1; i<=N; i++,puts("")) // for (int j=1; j<=N; j++) // printf("%d ",r[i][j]); // puts("=========================="); for (int i=1; i<=N; i++) for (int j=1; j<=N; j++) if (a[i][j]<=2*K){ // printf("%d %d %d %d\n",i-h[i][j]+1,l[i][j],i,r[i][j]); if (Sum(i-h[i][j]+1,l[i][j],i,r[i][j])>=K) Cut(i-h[i][j]+1,l[i][j],i,r[i][j]); } puts("NIE"); return 0; } /* 2 3 3 25 7 6 1 2 16 11 20 */
——It's a lonely path. Don't make it any lonelier than it has to be.