先上题目:
description
首先你们得承认今天的题目很短很简洁。。。
然后,你们还得承认接下来这个题目的描述更加简洁!!!
Task:给出一个N*N(1≤N≤2000)的矩阵,还给出一个整数K。要你在给定的矩阵中
求一个子矩阵,这个子矩阵中所有数的和的范围要在[k,2*k] 这个区间。
如果有多个这样的子矩阵,请随便输出一个。
Input
第一行包含两个整数K 和N(1≤K≤10^8,1≤N≤2000)。其意义如题目描述!
接下来有N 行,每行有N 个数,表示题目给出的矩阵。矩阵中的数都是非负数,而且
不大于maxlongint。
Output
输出文件仅包含一行,四个整数,分别是你找出来的矩阵的左上角坐标和右下角坐标。
如果不存在这样的子矩阵,请输出0 0 0 0。
Sample input1
4 3
1 1 1
1 9 1
1 1 1
Sample input2
8 4
1 2 1 3
25 1 2 1
4 20 3 3
3 30 12 2
Sample input3
8 4
12 2 1 3
25 1 2 1
4 20 3 3
3 30 12 2
Sample output1
0 0 0 0
Sample output2
1 2 2 4
Sample output3
1 1 1 1
Data Constraint
对于30%的数据,1≤N≤5
对于60%的数据,1≤N≤60
对于100%的数据1≤N≤2000
Time Limits:
1000 ms
总结:
这道题是今天比赛的最后一题,也是我唯一没有切的题。
有一个非常非常简略的题解,ACfast学长大概太相信我们的能力了。
在一堆苍蝇讨论了半天后,最后的结果是请清华神犇来讲讲,他把锅给我们初二背╮(╯▽╰)╭。
必备常识:
矩阵前缀和:f[i][j]=f[i-1][j]+f[i][j-1]-f[i-1][j-1]+a[i][j]
则a[a-c,b-d]=f[c][d]-f[c][b-1]-f[a-1][d]+f[a-1][b-1]
不要问为什么,自己画个图看。
首先由于元素都是非负数,所以若有a[i][j]>2k,那么我们选的矩阵一定不能包括它。
对于一个不包括>2k点的矩阵,如果它的矩阵和>=k,那么它一定有一个子矩阵的和sum满足k<=sum<=2k,请读者先仔细想想为什么,后面会解释。
现在需要求最大子矩阵(不包括>2k的点),由于元素非负,所以这个矩阵一定是嵌在>2k点和边界之间。
我们可以预处理r[i][j],表示(i,j)这个点向右遇到的第一个>2k的点,如果没有即为n+1。
枚举一个左边界l,利用单调栈维护一个使r[i][l]严格>=的东东,那么如果以当前r[i][l]-1作为右边界,上边界就是栈中下面的那一个i’-1,至于下边界现在还不知道。但是当我们遇到新的一个r[j][l]< r[i][l]时,要把r[i][l]退栈时,就知道了下边界是j-1。
注意:0,n+1要当作>2k的点丢进栈里。
那么我们得到了一个最大的矩阵,当sum>k时,如何找到那个符合条件的子矩阵呢?
要用到分治的思想。
如果sum<=2k,那我们可以愉快地直接输出答案。
如果不,我们把矩阵看成若干行,从边上开始每次减去一行。
1.这一行的和<=k,那我们一直减,总会遇到k<=sum<=2k,因为值不可能一次跳过这个范围。
2.这一行的和>=k,那更好,子矩阵就在这单行里,我们枚举一下列即可得出答案。
这道题就解决了。
Code:
#include<cstdio>
#define ll long long
#define fo(i,x,y) for(ll i=x;i<=y;i++)
#define fd(i,x,y) for(ll i=x;i>=y;i--)
using namespace std;
const ll maxn=2005;
ll k,n,bzans,a[maxn][maxn],s[maxn][maxn],r[maxn][maxn],d[maxn];
ll sum(ll a,ll b,ll x,ll y) {
return s[x][y]-s[x][b-1]-s[a-1][y]+s[a-1][b-1];
}
void solve2(ll a,ll b,ll x,ll y) {
fo(e,b,y) {
ll value=sum(a,b,a,e);
if(value >= k && value <= 2*k) {
printf("%lld %lld %lld %lld",a,b,a,e);
bzans=1;
}
if(bzans) return;
}
}
void solve(ll a,ll b,ll x,ll y) {
for(;sum(a,b,x,y) > 2*k && a!=x;) {
if(sum(x,b,x,y) >= 2*k)
solve2(x,b,x,y);
if(bzans) return;
x--;
}
if(a == x)
solve2(a,b,x,y);
if(bzans) return;
printf("%lld %lld %lld %lld",a,b,x,y);
bzans=1;
}
void pop(ll now,ll l) {
for(;d[0] > 0 && r[d[d[0]]][l] > r[now][l];) {
ll a=d[d[0]-1] + 1, b=l, x=now - 1, y=r[d[d[0]]][l] - 1;
if(sum(a,b,x,y) >= k)
solve(a,b,x,y);
if(bzans) return;
d[0]--;
}
}
void insert(ll now,ll l) {
pop(now,l);
d[++d[0]]=now;
}
int main() {
freopen("kup.in","r",stdin); freopen("kup.out","w",stdout);
scanf("%lld %lld", &k, &n);
fo(i,1,n)
fo(j,1,n) {
scanf("%lld", &a[i][j]);
s[i][j]=s[i-1][j]+s[i][j-1]-s[i-1][j-1]+a[i][j];
}
fo(i,1,n) {
r[i][n+1]=n+1;
fd(j,n,1)
if(a[i][j] > 2*k)
r[i][j]=j; else r[i][j]=r[i][j+1];
}
fo(l,1,n) {
d[0]=1; d[1]=0;
fo(i,1,n) {
insert(i,l);
if(bzans) return 0;
}
insert(n+1,l);
if(bzans) return 0;
}
printf("0 0 0 0");
}