传送门
好懵啊。。。又是一道没有代码就A不了的题。
首先可以发现,相邻的2个小矩阵的和为n*m。这提供了思路:将这种矩阵拼在一起。
可是我们发现,给的x与y所包含的不一定是偶数个小矩阵甚至有些都没有小矩阵,这就说明我们要分类讨论。
先设r与c为小矩阵组成的矩形的行列数。
- 绿(完整的小矩形组成的矩阵):发现,只有r与c都为奇数时会多出一个,而且在右下角。则此时ans = r * c / 2 * n * m + 余下矩阵ans。(除2要放前面因为这才与奇偶有关)
- 黄:其实前文规律不只对小矩形适用,对其余下的也适用。同理,发现会余下最后一个(奇数)。
- 红:无法配对,直接暴力计算。
思考一下如何编码:首先要用前缀和,因为用4个点做下标实在太多了不是吗?
上马吧!
#include<cstdio>
typedef long long ll;
const int N = 1002;
int n, m, q, r, c;
ll sum[N][N];
bool pd(const int x, const int y) {
return (__builtin_popcount(x) + __builtin_popcount(y)) & 1;
}
ll cal(const int x, const int y, bool f) {
return f == 0 ? sum[x][y] : x * y - sum[x][y];
}
ll O(const int x, const int y) {
return cal(x - r * n, y - c * m, pd(r, c));//B
}
ll R(const int x, const int y) {
ll res = 1ll * c / 2 * (x - r * n) * m;
if(c & 1)
res += cal(x - r * n, m, pd(r, c - 1));//A
return res;
}
ll C(const int x, const int y) {
ll res = 1ll * r / 2 * n * (y - c * m);
if(r & 1)
res += cal(n, y - c * m, pd(r - 1, c));
return res;
}
ll F(const int x, const int y) {
ll res = 1ll * r * c / 2 * n * m;
if((r & 1) && (c & 1))
res += cal(n, m, pd(r - 1, c - 1));
return res;
}
ll solve(const int x, const int y) {
if(x == 0 || y == 0)
return 0;
r = (x - 1) / n;
c = (y - 1) / m;
return O(x, y) + R(x, y) + C(x, y) + F(x, y);
}
int main() {
int a, b, c, d;
char ch;
scanf("%d %d %d", &n, &m, &q);
for(int i = 1; i <= n; i ++) {
scanf("\n");
for(int j = 1; j <= m; j ++) {
ch = getchar();
sum[i][j] = sum[i - 1][j] + sum[i][j - 1] - sum[i - 1][j - 1] + ch - '0';
}
}
while(q --) {
scanf("%d %d %d %d", &a, &b, &c, &d);
printf("%lld\n", solve(c, d) - solve(c, b - 1) - solve(a - 1, d) + solve(a - 1, b - 1));
}
return 0;
}
注:
B:手推发现,cal函数前2个变量表示的是那个多余矩阵的大小(既矩阵从(1,1)开始右下角的坐标)。pd函数和cal函数都是由大佬观察得出的,如果有人会证请一定评论好吗?(无力)pd函数含义:r行c列的矩阵的正或反。那么为什么r行c列矩阵表示红色那一坨呢?因为爱情 好吧其实是推出来的。
A:此函数用于计算行。之前说了,会留下最后一个,又因为m是固定的,r行c列前一个矩阵表示其。
好累啊,终于写完了。
如有错误,请大佬在评论区指出,谢谢!