P6070 『MdOI R1』Decrease

P6070 『MdOI R1』Decrease

题目

给定一个 \(n \times n\) 的矩阵,你可以进行若干次操作。

每次操作,你可以将一个 \(k \times k\)连续 子矩阵里的所有数全都加上 \(1\) 或者全都减去 \(1\)

初始时,矩阵中有 \(m\) 个位置上的数不为 \(0\),其它位置上的数均为 \(0\)

请你求出至少需要多少次操作,可以将矩形中所有数都变为 \(0\)

输入

第一行三个整数 \(n,m,k\),分别表示矩阵大小,非 \(0\) 格数和每次修改的连续子矩阵大小。

接下来 \(m\) 行,每行三个整数 \(x,y,z\),表示初始时矩阵的第 \(x\) 行第 \(y\) 列上的数为 \(z\)

输出

一行一个整数,表示最少操作次数。

特别地,如果无法使矩阵中所有数都变为 \(0\),输出 -1

样例 #1

输入

4 14 3
1 1 1
1 2 1
1 3 1
2 1 1
2 2 3
2 3 3
2 4 2
3 1 1
3 2 3
3 3 3
3 4 2
4 2 2
4 3 2
4 4 2

输出

3

样例 #2

输入

3 1 2
1 1 1

输出

-1

样例 #3

输入

4 5 1
1 1 5
2 2 -3
2 3 -4
3 3 1
4 4 2

输出

15

提示

【样例 1 解释】:

给出的矩阵为:

1 1 1 0
1 3 3 2
1 3 3 2
0 2 2 2

具体步骤:

先将以第一行第一列为左上角的连续子矩阵执行 减 1 操作 一次;

再将以第二行第二列为左上角的连续子矩阵执行 减 1 操作 两次。

总共三次。

1 1 1 0  0 0 0 0  0 0 0 0  0 0 0 0
1 3 3 2  0 2 2 2  0 1 1 1  0 0 0 0
1 3 3 2  0 2 2 2  0 1 1 1  0 0 0 0
0 2 2 2  0 2 2 2  0 1 1 1  0 0 0 0

【样例 2 解释】:

给出的矩阵为:

1 0 0
0 0 0
0 0 0

只通过 \(2\times 2\) 的连续子矩阵操作不可能使得所有格子上的数都变为 \(0\)

【数据范围】

本题采用捆绑测试。

子任务编号 \(n\leq\) \(k\leq\) 分值
1 \(10^3\) \(1\) 11
2 \(20\) \(20\) 14
3 \(100\) \(100\) 17
4 \(10^3\) \(10^3\) 34
5 \(5\times 10^3\) \(10^3\) 24

对于所有数据,\(1\leq n\leq 5\times 10^3\)\(1\leq m\leq \min(n^2,5\times 10^5)\)\(1\leq k\leq \min(n,10^3)\)\(1\leq x,y\leq n\),每对 \((x,y)\) 至多出现一次,\(1 \le |z| \leq 10^9\)

数据保证如果有解,答案不超过 \(2^{63}-1\)

【提示】

本题读入量较大,建议使用较快的读入方式。


思路

分析数据范围,时间复杂度为 \(O(n^2k^2)\) 的暴力写法只能拿部分分数。题目中“可以将一个 \(k \times k\) 的子矩阵全部加上 \(1\) 或者全部减去 \(1\)”。借鉴二维差分的思想:连续位置数值增减修改。假设矩阵中每个值为 \(f_{i,j}\),如果想对 \(f_{1,1}\) 做修改,就只能对 \(f_{1,1}\)\(f_{k,k}\) 这一个大矩阵进行修改。如果想对 \(f_{1,2}\) 做修改,由于不能改变 \(f_{1,1}\) 的值,所以只能改动 \(f_{1,2}\)\(f_{k,k+2}\) 这个矩阵的值。以此类推,只要把当前数消为 \(0\) 就可以了。假设差分数组为 \(cf \lbrack \rbrack\),要将 \((i,j)\) 位置的数字消为 \(0\),则

\[cf_{i+k,j+k}-=cf_{i,j},cf_{i+k,j}+=cf_{i,j},cf_{i,j+k}+=cf_{i,j}(i+k \le n,j+k \le n) \]

\(cf_{i,j}\) 表示将 \((i,j)\) 位置数字消为 \(0\) 的操作数,将其绝对值进行累加即为总操作数。最后判断是否矩阵中所有的数字都被消为 \(0\) 即可。

代码

#include <bits/stdc++.h>

using namespace std;

int n, m, k, x, y, z, a[5010][5010], cf[5010][5010];
long long ans;

int main()
{
	scanf("%d %d %d", &n, &m, &k);
	while (m -- )
	{
		scanf("%d %d %d", &x, &y, &z);
		a[x][y] = z;
	}
	for (int i = 1; i <= n; i ++ )
	{
		for (int j = 1; j <= n; j ++ )
			cf[i][j] = a[i][j] - a[i - 1][j] - a[i][j - 1] + a[i - 1][j - 1];
	}
	for (int i = 1; i <= n; i ++ )
	{
		for (int j = 1; j <= n; j ++ )
		{
			ans += abs(cf[i][j]); // 记得加绝对值
			if (i + k <= n + 1 && j + k <= n + 1)
			{
				cf[i + k][j] += cf[i][j];
				cf[i][j + k] += cf[i][j];
				cf[i + k][j + k] -= cf[i][j];
				cf[i][j] -= cf[i][j]; // 注意:放在循环最后
			}
		}
	}
	for (int i = 1; i <= n; i ++ )
	{
		for (int j = 1; j <= n; j ++ )
		{
			if (cf[i][j] != 0)
			{
				puts("-1");
				return 0;
			}
		}
	}
	printf("%lld", ans);
	return 0;
}
posted @ 2024-05-01 12:42  IronMan_PZX  阅读(25)  评论(0)    收藏  举报
Title