将行和列分开考虑。
在每组询问 (x1,y1,x2,y2)(x_1,y_1,x_2,y_2)(x1,y1,x2,y2) 中:
-
对于每一行,相邻的两个数的下标差为 111。
-
对于每一列,相邻的两个数的下标差为 y2−y1+1y_2 - y_1 + 1y2−y1+1。
不难想到对行和列分别做 i×ai,ji \times a_{i,j}i×ai,j 和 j×ai,jj \times a_{i,j}j×ai,j 的前缀和处理。由于列的下标之差不为 111,所以需要先对其做扩大倍数的处理后再将其相加。设最终要求的 ∑iai\sum i a_i∑iai 的 iii 为系数,那么对于一组询问,可以得到以下系数矩阵:
[y1+x1(y2−y1+1)y1+1+x1(y2−y1+1)⋯y2+x1(y2−y1+1)y1+(x1+1)(y2−y1+1)y1+1+(x1+1)(y2−y1+1)⋯y2+(x1+1)(y2−y1+1)⋮⋮⋮⋮y1+x2(y2−y1+1)y1+1+x2(y2−y1+1)⋯y2+x2(y2−y1+1)] \begin{bmatrix} y_1 + x_1(y_2 - y_1 + 1) & y_1 + 1 + x_1(y_2 - y_1 + 1) & \cdots & y_2 + x_1(y_2 - y_1 + 1)\\ y_1 + (x_1 + 1)(y_2 - y_1 + 1) & y_1 + 1 + (x_1 + 1)(y_2 - y_1 + 1) & \cdots & y_2 + (x_1 + 1)(y_2 - y1 + 1)\\ \vdots & \vdots & \vdots & \vdots\\ y_1 + x_2(y_2 - y_1 + 1) & y_1 + 1 + x_2(y_2 - y_1 + 1) & \cdots & y_2 + x_2(y_2 - y_1 + 1) \end{bmatrix} y1+x1(y2−y1+1)y1+(x1+1)(y2−y1+1)⋮y1+x2(y2−y1+1)y1+1+x1(y2−y1+1)y1+1+(x1+1)(y2−y1+1)⋮y1+1+x2(y2−y1+1)⋯⋯⋮⋯y2+x1(y2−y1+1)y2+(x1+1)(y2−y1+1)⋮y2+x2(y2−y1+1)
然而正确的系数矩阵应为:
[12⋯y2−y1+1y2−y1+2y2−y1+3⋯2(y2−y1+1)⋮⋮⋮⋮(x2−x1)(y2−y1+1)+1(x2−x1)(y2−y1+1)+2⋯(x2−x1+1)(y2−y1+1)] \begin{bmatrix} 1 & 2 & \cdots & y_2 - y_1 + 1\\ y_2 - y_1 + 2 & y_2 - y_1 + 3 & \cdots & 2(y_2 - y_1 + 1)\\ \vdots & \vdots & \vdots & \vdots\\ (x_2 - x_1)(y_2 - y_1 + 1) + 1 & (x_2 - x_1)(y_2 - y_1 + 1) + 2 & \cdots & (x_2 - x_1 + 1)(y_2 - y_1 + 1) \end{bmatrix} 1y2−y1+2⋮(x2−x1)(y2−y1+1)+12y2−y1+3⋮(x2−x1)(y2−y1+1)+2⋯⋯⋮⋯y2−y1+12(y2−y1+1)⋮(x2−x1+1)(y2−y1+1)
将矩阵中的每一个数字都减去 x1(y2−y1+1)+y1−1x_1(y_2 - y_1 + 1) + y_1 - 1x1(y2−y1+1)+y1−1 次即可,也就是再维护一个普通的二维前缀和后处理。总时间复杂度为 O(t(n2+q))O(t(n^2 + q))O(t(n2+q)),代码如下:
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstring>
#define init(x) memset (x,0,sizeof (x))
#define ll long long
#define ull unsigned long long
#define INF 0x3f3f3f3f
using namespace std;
const int MAX = 2e3 + 5;
const int MOD = 1e9 + 7;
inline int read ();
int t,n,q;
ll sum[MAX][MAX],sumx[MAX][MAX],sumy[MAX][MAX];
int main ()
{
//freopen (".in","r",stdin);
//freopen (".out","w",stdout);
t = read ();
while (t--)
{
n = read ();q = read ();
for (int i = 1;i <= n;++i)
{
for (int j = 1;j <= n;++j)
{
int x = read ();
sum[i][j] = x + sum[i - 1][j] + sum[i][j - 1] - sum[i - 1][j - 1];
sumx[i][j] = i * x + sumx[i - 1][j] + sumx[i][j - 1] - sumx[i - 1][j - 1];
sumy[i][j] = j * x + sumy[i - 1][j] + sumy[i][j - 1] - sumy[i - 1][j - 1];;
}
}
while (q--)
{
int sx = read (),sy = read (),fx = read (),fy = read ();
ll s = sum[fx][fy] - sum[sx - 1][fy] - sum[fx][sy - 1] + sum[sx - 1][sy - 1];
ll sum_x = sumx[fx][fy] - sumx[sx - 1][fy] - sumx[fx][sy - 1] + sumx[sx - 1][sy - 1];
ll sum_y = sumy[fx][fy] - sumy[sx - 1][fy] - sumy[fx][sy - 1] + sumy[sx - 1][sy - 1];
//同一列的公差为 fy - sy + 1
printf ("%lld ",sum_x * (fy - sy + 1) + sum_y - s * (1ll * sx * (fy - sy + 1) + sy - 1));
}
puts ("");
}
return 0;
}
inline int read ()
{
int s = 0;int f = 1;
char ch = getchar ();
while ((ch < '0' || ch > '9') && ch != EOF)
{
if (ch == '-') f = -1;
ch = getchar ();
}
while (ch >= '0' && ch <= '9')
{
s = s * 10 + ch - '0';
ch = getchar ();
}
return s * f;
}

被折叠的 条评论
为什么被折叠?



