P9583 题解

题目传送门

思路

我们首先可以考虑纯模拟(每次操作暴力枚举修改),当然这样时间复杂度是 O(nq)O(nq),肯定太慢了。

既然不能暴力枚举修改,那我们将所有修改 xx 行的次数记为 sum1xsum1_x,将所有修改 yy 列的次数记为 sum2ysum2_y,最后枚举每个 iijj 列的点,如果这个点上有颜色,则涂的次数肯定kk 的倍数,而涂的次数是 sum1i+sum2jsum1_i+sum2_j,所以只要统计出所有满足 (sum1i+sum2j)modk0(sum1_i+sum2_j)\bmod k\ne0 的点的个数即可。

但是这样还是太慢了。我们可以考虑反过来想,即所有点的个数减去不满足要求的点个数。不满足要求,就是指 (sum1i+sum2j)modk=0(sum1_i+sum2_j)\bmod k=0

我们设 x=sum1imodk,y=sum2jmodkx=sum1_i\bmod k,y=sum2_j\bmod k,如果 x,yx,y 都为 00x+y=kx+y=k,则这个点不满足要求,我们可以定义两个桶 tot1tot1tot2tot2tot1itot1_i 表示对于所有 sum1jsum1_jsum1jmodk=isum1_j\bmod k=i 的个数,tot2itot2_i 同理。根据乘法原理,所有答案就是 tot1i×tot2(ki)modktot1_i\times tot2_{(k-i)\bmod k}(当然你也可以把 i=0i=0 的情况特殊处理)。最后别忘了答案要求的是合法个数而不是不合法个数,我们要用总数减去不合法个数。

代码

# include <bits/stdc++.h>
using namespace std;
typedef long long ll; //不开 long long 见祖宗
ll n, m, q, k, op, x, sum1[200005], sum2[200005], sum, tot1[500005], tot2[500005];
int main () {
	cin >> n >> m >> q >> k;
	while (q --) {
		cin >> op >> x;
		if (op < 2)
			++ sum1[x];
		else
			++ sum2[x];
	}
	for (ll i = 1; i <= n; ++ i)
		++ tot1[sum1[i] % k];
	for (ll i = 1; i <= m; ++ i)
		++ tot2[sum2[i] % k];
	for (ll i = 1; i < k; ++ i)
		sum += tot1[i] * tot2[k - i];
	cout << n * m - sum - tot1[0] * tot2[0]; //总数减不合法个数
	return 0;
}
posted @ 2023-08-27 12:48  Vitamin_B  阅读(13)  评论(0)    收藏  举报  来源