题目描述:
给你一个n*n的矩形方格(横纵坐标都为
0
−
(
n
−
1
)
0-(n-1)
0−(n−1)),一开始每个方格中的数都为 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=1∑xj=1∑ya[i][j]然后我们用b[i][j]来做差分数组,由于差分数组做前缀和就是原数,显然:原式=i=1∑xj=1∑yk=1∑il=1∑j b[k][l]而这个式子求得就是整个矩阵的和,显然可以看成单点的值的和,我们将后两个∑拆开得到:原式=i=1∑xj=1∑y(x−i+1)(y−j+1) b[i][j](可抽象在一个点在矩阵上被多少个矩阵覆盖来理解)经过拆分移项得到:原式=(xy+x+y)i=1∑xj=1∑yb[i][j]+i=1∑xj=1∑y(ij−i−j+1)b[i][j]−x∗i=1∑xj=1∑yj∗b[i][j]−y∗i=1∑xj=1∑yi∗b[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],(ij−i−j+1)b[i][j],j∗b[i][j],i∗b[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;
}