CF2044H Hard Demon Problem

将行和列分开考虑。

在每组询问 (x1,y1,x2,y2)(x_1,y_1,x_2,y_2)(x1,y1,x2,y2) 中:

  • 对于每一行,相邻的两个数的下标差为 111

  • 对于每一列,相邻的两个数的下标差为 y2−y1+1y_2 - y_1 + 1y2y1+1

不难想到对行和列分别做 i×ai,ji \times a_{i,j}i×ai,jj×ai,jj \times a_{i,j}j×ai,j 的前缀和处理。由于列的下标之差不为 111,所以需要先对其做扩大倍数的处理后再将其相加。设最终要求的 ∑iai\sum i a_iiaiiii 为系数,那么对于一组询问,可以得到以下系数矩阵:

[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(y2y1+1)y1+(x1+1)(y2y1+1)y1+x2(y2y1+1)y1+1+x1(y2y1+1)y1+1+(x1+1)(y2y1+1)y1+1+x2(y2y1+1)y2+x1(y2y1+1)y2+(x1+1)(y2y1+1)y2+x2(y2y1+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} 1y2y1+2(x2x1)(y2y1+1)+12y2y1+3(x2x1)(y2y1+1)+2y2y1+12(y2y1+1)(x2x1+1)(y2y1+1)

将矩阵中的每一个数字都减去 x1(y2−y1+1)+y1−1x_1(y_2 - y_1 + 1) + y_1 - 1x1(y2y1+1)+y11 次即可,也就是再维护一个普通的二维前缀和后处理。总时间复杂度为 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;
}
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值