难题
3
/
10
3/10
3/10
裸题经典题
题意
给你一个
n
,
m
n,m
n,m 的矩阵。
你每次可以选择一个
a
,
b
a,b
a,b 的矩阵,每个元素都同时减去
1
1
1。
问你经过一些操作之后,能否得到全
0
0
0 的矩阵。
数据范围
样例组数
T
≤
100
T\le 100
T≤100
n
,
m
≤
1000
n,m\le 1000
n,m≤1000
a
,
b
≤
1000
a,b\le 1000
a,b≤1000
思路
对于最最上角的非零元素
M
i
,
j
M_{i,j}
Mi,j,一定是以它为左上角的
(
a
,
b
)
(a,b)
(a,b) 的矩阵
矩阵中每个元素都减去
M
i
,
j
M_{i,j}
Mi,j
非法情况:如果遇到无法选择的矩阵(以它为左上角无法选出 ( a , b ) (a,b) (a,b)的矩阵的情况)或者元素减掉之后变为负数了,都是无解。
但是光遍历矩阵元素就已经 O ( n m ) O(nm) O(nm) 了,很明显无法直接遍历的方法来让选出的 ( a , b ) (a,b) (a,b)矩阵的元素减少。
这时候,我们想到一维差分,但是明显会
O
(
n
2
m
)
O(n^2m)
O(n2m),明显也会超时。
这时候,我们想到二维差分,明显就
O
(
n
m
)
O(nm)
O(nm) 了,但是不大会。
【正题】
对于一个二维差分数组
d
e
[
i
]
[
j
]
de[i][j]
de[i][j],如果要对左上角坐标为
(
x
,
y
)
(x,y)
(x,y)
矩阵大小为
a
,
b
a,b
a,b
右下角坐标为
(
x
+
a
−
1
,
y
+
b
−
1
)
(x+a-1,y+b-1)
(x+a−1,y+b−1) 的矩阵的元素同时增加
c
c
c
那么代码如下:
void ins(int x,int y,ll c,int a,int b){
int dx = x + a;
int dy = y + b;
de[x][y] -= c;
de[x][dy] += c;
de[dx][y] += c;
de[dx][dy] -= c;
}
然后差分数组,获取 ( i , j ) (i,j) (i,j) 的元素值:
de[i][j] = de[i][j] + de[i][j-1] + de[i-1][j] - de[i-1][j-1];
为什么画个图就知道了->
由于循环从左上到右下遍历,故差分数组表示该点到右下角的矩阵元素和
红色区域加C,绿色蓝色区域减C,黄色区域加C就等价于中间区间加C
核心代码
时间复杂度
O
(
n
m
)
O(nm)
O(nm)
空间复杂度
O
(
n
m
)
O(nm)
O(nm)
const int MAX = 1e3+50;
ll aa[MAX][MAX];
ll de[MAX][MAX];
void ins(int x,int y,ll c,int a,int b){
int dx = x + a;
int dy = y + b;
de[x][y] -= c;
de[x][dy] += c;
de[dx][y] += c;
de[dx][dy] -= c;
}
int main()
{
int T;
scanf("%d",&T);
while(T--){
int n,m,a,b;
scanf("%d%d%d%d",&n,&m,&a,&b);
for(int i = 0;i <= n+1;++i)
for(int j = 0;j <= m+1;++j)
de[i][j] = 0;
for(int i = 1;i <= n;++i)
for(int j = 1;j <= m;++j){
scanf("%d",&aa[i][j]);
ins(i,j,-aa[i][j],1,1); /// 负号意味着加上
}
bool flag = true;
for(int i = 1;flag && i <= n;++i){
for(int j = 1;flag && j <= m;++j){
de[i][j] = de[i][j] + de[i][j-1] + de[i-1][j] - de[i-1][j-1];
if(de[i][j] == 0)continue;
if(de[i][j] < 0)flag = false;
if(de[i][j] > 0){
if(i + a - 1 > n || j + b - 1 > m)flag = false;
else{
ins(i,j,de[i][j],a,b); /// 正号意味着减去
}
}
}
}
if(flag)puts("^_^");
else puts("QAQ");
}
return 0;
}