【树状数组 && 公式推导】 普转提七联测 A+B Problem

题目描述:

给你一个n*n的矩形方格(横纵坐标都为 0 − ( n − 1 ) 0-(n-1) 0(n1)),一开始每个方格中的数都为 0 。你需要支持以下 两个操作:
1.将给定的一个矩形中所有数+1。
2.询问给定一个矩形内所有数之和对p取模。
并强制在线。

在这里插入图片描述


Solution

首先我们可以推出一个式子暴力求出矩阵的和:
∑ i = 1 x ∑ j = 1 y a [ i ] [ j ] 然 后 我 们 用 b [ i ] [ j ] 来 做 差 分 数 组 , 由 于 差 分 数 组 做 前 缀 和 就 是 原 数 , 显 然 : 原 式 = ∑ i = 1 x ∑ j = 1 y ∑ k = 1 i ∑ l = 1 j   b [ k ] [ l ] 而 这 个 式 子 求 得 就 是 整 个 矩 阵 的 和 , 显 然 可 以 看 成 单 点 的 值 的 和 , 我 们 将 后 两 个 ∑ 拆 开 得 到 : 原 式 = ∑ i = 1 x ∑ j = 1 y ( x − i + 1 ) ( y − j + 1 )   b [ i ] [ j ] ( 可 抽 象 在 一 个 点 在 矩 阵 上 被 多 少 个 矩 阵 覆 盖 来 理 解 ) 经 过 拆 分 移 项 得 到 : 原 式 = ( x y + x + y ) ∑ i = 1 x ∑ j = 1 y b [ i ] [ j ] + ∑ i = 1 x ∑ j = 1 y ( i j − i − j + 1 ) b [ i ] [ j ] − x ∗ ∑ i = 1 x ∑ j = 1 y j ∗ b [ i ] [ j ] − y ∗ ∑ i = 1 x ∑ j = 1 y i ∗ b [ i ] [ j ] \sum_{i=1}^x\sum_{j=1}^ya[i][j] \\然后我们用b[i][j]来做差分数组,由于差分数组做前缀和就是原数,显然:\\ 原式=\sum_{i=1}^x\sum_{j=1}^y\sum_{k=1}^i\sum_{l=1}^j\ b[k][l]\\而这个式子求得就是整个矩阵的和,显然可以看成单点的值的和,我们将后两个\sum拆开得到:\\原式=\sum_{i=1}^x\sum_{j=1}^y(x-i+1)(y-j+1)\ b[i][j](可抽象在一个点在矩阵上被多少个矩阵覆盖来理解)\\经过拆分移项得到:\\原式=(xy+x+y)\sum_{i=1}^x\sum_{j=1}^yb[i][j]+\sum_{i=1}^x\sum_{j=1}^y(ij-i-j+1)b[i][j]\\-x*\sum_{i=1}^x\sum_{j=1}^y j*b[i][j]-y*\sum_{i=1}^x\sum_{j=1}^yi*b[i][j] i=1xj=1ya[i][j]b[i][j]=i=1xj=1yk=1il=1j b[k][l]=i=1xj=1y(xi+1)(yj+1) b[i][j]()=(xy+x+y)i=1xj=1yb[i][j]+i=1xj=1y(ijij+1)b[i][j]xi=1xj=1yjb[i][j]yi=1xj=1yib[i][j]

对于这个式子,我们是需要用四个二维树状数组来维护 b [ i ] [ j ] , ( i j − i − j + 1 ) b [ i ] [ j ] , j ∗ b [ i ] [ j ] , i ∗ b [ i ] [ j ] 即 可 b[i][j],(ij-i-j+1)b[i][j],j*b[i][j],i*b[i][j]即可 b[i][j],(ijij+1)b[i][j],jb[i][j],ib[i][j]


Code

#include<bits/stdc++.h>
using namespace std;
int n,q,p,r=0;

struct Tr{
	short int t[8002][8002];
	void add(int x,int y,int v){
		v%=p;
		for (int i=x;i<=n;i+=i&(-i))
		  for (int j=y;j<=n;j+=j&(-j)) t[i][j] = (t[i][j] + v)%p;
	}
	int sum(int x,int y){
		int ans = 0;
		for (int i=x;i;i-=i&(-i))
		  for (int j=y;j;j-=j&(-j)) ans = (ans + t[i][j])%p;
		return ans;
	}
};

Tr t1,t2,t3,t4;

int gb(int x,int y){
	return (x*y-x-y+1) % p;
}

void change(int a,int c,int b,int d){
	t1.add(a,b,1);
	t1.add(a,d+1,-1);
	t1.add(c+1,b,-1);
	t1.add(c+1,d+1,1);//第一种
	
	t2.add(a,b,gb(a,b));
	t2.add(a,d+1,-gb(a,d+1));
	t2.add(c+1,b,-gb(c+1,b));
	t2.add(c+1,d+1,gb(c+1,d+1));//第二种
	
	t3.add(a,b,b);
	t3.add(a,d+1,-(d+1));
	t3.add(c+1,b,-(b));
	t3.add(c+1,d+1,d+1);//第三种
	
	t4.add(a,b,a);
	t4.add(a,d+1,-a);
	t4.add(c+1,b,-(c+1));
	t4.add(c+1,d+1,c+1);//第四种
	return ;
}

int ask(int x,int y){
	if (!x || !y) return 0;
	int s1 = (x*y+x+y) * 1ll * t1.sum(x,y) % p;//记得乘上洗漱
	int s2 = t2.sum(x,y) % p;
	int s3 = x * 1ll * t3.sum(x,y) % p;
	int s4 = y * 1ll * t4.sum(x,y) % p;
	int ans = (s1+s2-s3-s4)%p;
	return (ans+p)%p;//需要注意
}

int query(int a,int c,int b,int d){
	int s1 = ask(c,d);
	int s2 = ask(a-1,d);
	int s3 = ask(c,b-1);
	int s4 = ask(a-1,b-1);
	int ans = (s1-s2-s3+s4)%p;//二维前缀和套路
	return (ans+p)%p;
}

int main(){
	freopen("fourth.in","r",stdin);
	freopen("fourth.out","w",stdout);
	scanf("%d %d %d",&n,&q,&p);
	while (q--){
		int ty,w,x,y,z;
		scanf("%d %d %d %d %d",&ty,&w,&x,&y,&z);
		int a,b,c,d;
		a = (w + r) % n; 
		b = (x + r) % n; 
		c = (y + r) % n; 
		d = (z + r) % n; 
		if (b < a) swap(a,b); 
		if (d < c) swap(c,d);
		a++,b++,c++,d++;
		if (ty == 1) change(a,b,c,d);//修改
		else{
			int ans = query(a,b,c,d);
			r += ans;
			printf("%d\n",ans);
		}
	}
	return 0;
} 
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值