树状数组
线段树
1.父节点
⌊
x
2
⌋
\lfloor \frac{x}{2} \rfloor
⌊2x⌋
x
x
x>>1
2.左儿子 2
x
x
x
x
x
x<<1
3.右儿子 2
x
x
x+1
x
x
x<<1|1
动态求连续区间和
题目大意
给定 n 个数组成的一个数列,规定有两种操作,一是修改某个元素,二是求子数列 [a,b] 的连续和。
输入格式
第一行包含两个整数 n 和 m,分别表示数的个数和操作次数。
第二行包含 n 个整数,表示完整数列。
接下来 m 行,每行包含三个整数 k,a,b (k=0,表示求子数列[a,b]的和;k=1,表示第 a 个数加 b)。数列从 1 开始计数。
输出格式
输出若干行数字,表示 k=0 时,对应的子数列 [a,b] 的连续和。
数据范围
1≤n≤100000
1≤m≤100000
1≤a≤b≤n
输入样例
10 5
1 2 3 4 5 6 7 8 9 10
1 1 5
0 1 3
0 4 8
1 7 5
0 4 8
输出样例
11
30
35
树状数组
#include <cstdio>
#include <algorithm>
using namespace std;
const int maxn=1e5+7;
int a[maxn],tree[maxn<<2];
int lowbit(int x) //返回最低位的1及其后边所有的0构成的数值
{
return x&-x;
}
void add(int l,int r,int v) //单点修改
{
for(int i=l;i<=r;i+=lowbit(i)) tree[i]+=v;
}
int find(int x) //区间查询
{
int res=0;
for(int i=x;i;i-=lowbit(i)) res+=tree[i];
return res;
}
int main()
{
int n,m;
scanf("%d%d",&n,&m);
for(int i=1;i<=n;++i) scanf("%d",&a[i]);
for(int i=1;i<=n;++i) add(i,n,a[i]);
while(m--)
{
int op,l,r;
scanf("%d%d%d",&op,&l,&r);
if(op) add(l,n,r);
else printf("%d\n",find(r)-find(l-1));
}
return 0;
}
线段树
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 100010;
int n, m;
int w[N];
struct Node
{
int l, r;
int sum;
}tree[N<<2];
void pushup(int rt)//区间和
{
tree[rt].sum = tree[rt << 1].sum + tree[rt << 1 | 1].sum;
}
void build(int rt, int L, int R) //构造线段树
{
tree[rt].l=L,tree[rt].r=R;
if (L == R)
{
tree[rt].sum=w[L];
return ;
}
int mid = (L + R) >> 1;
build(rt << 1, L, mid); //递归构建左子树
build(rt << 1 | 1, mid + 1, R); //递归构建右子树
pushup(rt); //区间和
}
void add(int rt, int x, int v) //区间和
{
int l=tree[rt].l,r=tree[rt].r;
if (l == r)
{
tree[rt].sum += v;
return;
}
int mid = (l+r) >> 1;
if (x <= mid) add(rt << 1, x, v); //左半边
else add(rt << 1 | 1, x, v); //右半边
pushup(rt);
}
int Query(int rt, int L,int R) //查询操作
{
int l=tree[rt].l, r=tree[rt].r;
if (l >= L && r<= R) return tree[rt].sum;
int sum=0, mid=(l+r)>>1;
if(L<=mid) sum+=Query(rt<<1,L,R); //和左区间有交集
if(R>mid) sum+=Query(rt<<1|1,L,R); //和右区间有交集
return sum;
}
int main()
{
scanf("%d%d", &n, &m);
for (int i = 1; i <= n; i ++ ) scanf("%d", &w[i]);
build(1, 1, n);
int op, a, b;
while (m -- )
{
scanf("%d%d%d", &op, &a, &b);
if (op) add(1, a, b);
else printf("%d\n", Query(1, a, b));
}
return 0;
}
数星星
题目大意
天空中有一些星星,这些星星都在不同的位置,每个星星有个坐标。如果一个星星的左下方(包含正左和正下)有 k 颗星星,就说这颗星星是 k 级的。
例如,上图中星星 5 是 3 级的(1,2,4 在它左下),星星 2,4 是 1 级的。例图中有 1 个 0 级,2 个 1 级,1 个 2 级,1 个 3 级的星星。给定星星的位置,输出各级星星的数目。换句话说,给定 N 个点,定义每个点的等级是在该点左下方(含正左、正下)的点的数目,试统计每个等级有多少个点。
输入格式
第一行一个整数 N,表示星星的数目;接下来 N 行给出每颗星星的坐标,坐标用两个整数 x,y 表示;不会有星星重叠。星星按 y 坐标增序给出,y 坐标相同的按 x 坐标增序给出。
输出格式
N 行,每行一个整数,分别是 0 级,1 级,2 级,……,N−1 级的星星的数目。
数据范围
1≤N≤15000
0≤x,y≤32000
输入样例
5
1 1
5 1
7 1
3 3
5 5
输出样例
1
2
1
1
0
#include <cstdio>
#include <algorithm>
using namespace std;
const int maxn=35000;
int a[maxn],tree[maxn];
int lowbit(int x)
{
return x&-x;
}
void add(int l)
{
for(int i=l;i<maxn;i+=lowbit(i)) tree[i]++;
}
int find(int x)
{
int res=0;
for(int i=x;i;i-=lowbit(i)) res+=tree[i];
return res;
}
int main()
{
int n;
scanf("%d",&n);
for(int i=0;i<n;++i)
{
int x,y;
scanf("%d%d",&x,&y);
x++; //树状数组的下标必须从1开始
//输出a[1 ~ n],因为每个星星的等级都提高了一级
a[find(x)]++; // a[i]+1
add(x); //前a[1~x]
}
for(int i=0;i<n;++i) printf("%d\n",a[i]);
return 0;
}
数列区间最大值
题目大意
输入一串数字,给你 M 个询问,每次询问就给你两个数字 X,Y,要求你说出 X 到 Y 这段区间内的最大数。
输入格式
第一行两个整数 N,M 表示数字的个数和要询问的次数;
接下来一行为 N 个数;
接下来 M 行,每行都有两个整数 X,Y。
输出格式
输出共 M 行,每行输出一个数。
数据范围
1≤N≤105,
1≤M≤106,
1≤X≤Y≤N,
数列中的数字均不超过2
31
^{31}
31−1
输入样例
10 2
3 2 4 5 6 8 1 2 9 7
1 4
3 8
输出样例
5
8
#include <cstdio>
#include <algorithm>
#include <climits>
using namespace std;
const int maxn=1e5+7;
int a[maxn];
struct node
{
int l,r,maxx;
}tree[maxn<<2];
void build(int rt,int L,int R)
{
tree[rt].l=L,tree[rt].r=R;
if(L>=R)
{
tree[rt].maxx=a[L];
return;
}
int mid=(L+R)>>1;
build(rt<<1,L,mid);
build(rt<<1|1,mid+1,R);
tree[rt].maxx=max(tree[rt<<1].maxx,tree[rt<<1|1].maxx);
}
int find(int rt,int L,int R)
{
int l=tree[rt].l,r=tree[rt].r;
if(l>=L&&r<=R) return tree[rt].maxx;
int mid=(l+r)>>1; //树中的中点
int ans=-0x3f3f3f3f;
if(L<=mid) ans=max(ans,find(rt<<1,L,R));
if(R>mid) ans=max(ans,find(rt<<1|1,L,R));
return ans;
}
int main()
{
int n,m;
scanf("%d%d",&n,&m);
for(int i=1;i<=n;++i) scanf("%d",&a[i]);
build(1,1,n);
while(m--)
{
int x,y;
scanf("%d%d",&x,&y);
printf("%d\n",find(1,x,y));
}
return 0;
}
小朋友排队
题目大意
n 个小朋友站成一排。现在要把他们按身高从低到高的顺序排列,但是每次只能交换位置相邻的两个小朋友。每个小朋友都有一个不高兴的程度。开始的时候,所有小朋友的不高兴程度都是 0。如果某个小朋友第一次被要求交换,则他的不高兴程度增加 1,如果第二次要求他交换,则他的不高兴程度增加 2(即不高兴程度为 3),依次类推。当要求某个小朋友第 k 次交换时,他的不高兴程度增加 k。请问,要让所有小朋友按从低到高排队,他们的不高兴程度之和最小是多少。如果有两个小朋友身高一样,则他们谁站在谁前面是没有关系的。
输入格式
输入的第一行包含一个整数 n,表示小朋友的个数。
第二行包含 n 个整数 H
1
_1
1,H
2
_2
2,…,H
n
_n
n,分别表示每个小朋友的身高。
输出格式
输出一行,包含一个整数,表示小朋友的不高兴程度和的最小值。
数据范围:1≤n≤100000,0≤H
i
_i
i≤1000000
输入样例
3
3 2 1
输出样例
9
样例解释:首先交换身高为3和2的小朋友,再交换身高为3和1的小朋友,再交换身高为2和1的小朋友,每个小朋友的不高兴程度都是3,总和为9
#include <cstdio>
#include <cstring>
using namespace std;
typedef long long ll;
const int maxn=1e6+7;
ll tree[maxn],h[maxn],res[maxn];
ll lowbit(int x)
{
return x&-x;
}
void add(int x,int v)
{
for(int i=x;i<maxn;i+=lowbit(i)) tree[i]+=v;
}
ll find(int x)
{
ll cnt=0;
for(int i=x;i;i-=lowbit(i)) cnt+=tree[i];
return cnt;
}
int main()
{
int n;
scanf("%d",&n);
for(int i=0;i<n;++i) scanf("%lld",&h[i]),h[i]++;
// 求每个数前面有多少个数比它大,从前往后数,只有遍历过的才会被统计。
for(int i=0;i<n;++i)
{
res[i]=find(maxn)-find(h[i]);
add(h[i],1);
}
// 每个数后面有多少个数比它小
memset(tree, 0, sizeof(tree));
for (int i = n - 1; i >= 0; i -- )
{
res[i] += find(h[i] - 1);
add(h[i], 1);
}
ll ans = 0;
for (int i = 0; i < n; i ++ ) ans += res[i] * (res[i] + 1) / 2;
printf("%lld\n",ans);
return 0;
}
油漆面积
题目大意
X星球的一批考古机器人正在一片废墟上考古。该区域的地面坚硬如石、平整如镜。管理人员为方便,建立了标准的直角坐标系。每个机器人都各有特长、身怀绝技。它们感兴趣的内容也不相同。经过各种测量,每个机器人都会报告一个或多个矩形区域,作为优先考古的区域。矩形的表示格式为 (
x
1
x_1
x1,
y
1
y_1
y1,
x
2
x_2
x2,
y
2
y_2
y2),代表矩形的两个对角点坐标。为了醒目,总部要求对所有机器人选中的矩形区域涂黄色油漆。小明并不需要当油漆工,只是他需要计算一下,一共要耗费多少油漆。其实这也不难,只要算出所有矩形覆盖的区域一共有多大面积就可以了。注意,各个矩形间可能重叠。
输入格式
第一行,一个整数 n,表示有多少个矩形。
接下来的 n 行,每行有 4 个整数
x
1
x_1
x1,
y
1
y_1
y1,
x
2
x_2
x2,
y
2
y_2
y2,空格分开,表示矩形的两个对角顶点坐标。
输出格式
一行一个整数,表示矩形覆盖的总面积。
数据范围:1≤n≤10000,0≤
x
1
x_1
x1,
x
2
x_2
x2,
y
1
y_1
y1,
y
2
y_2
y2≤10000
数据保证
x
1
x_1
x1<
x
2
x_2
x2 且
y
1
y_1
y1<
y
2
y_2
y2。
输入样例
3
1 5 10 10
3 1 20 20
2 7 15 17
输出样例
340
线段树+扫描线
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
const int maxn = 10010;
int n;
struct Segment
{
int x; //横坐标
int y1, y2; //纵坐标
int k; //加的值是正1还是负1
bool operator< (const Segment &t)const //按照横坐标排序
{
return x < t.x;
}
}seg[maxn<<1];
struct Node
{
int l, r; //左右边界
int cnt; //当前区间被覆盖次数
int len; //至少被覆盖一次的区间长度
}tree[maxn<<2];
void pushup(int rt)
{
if (tree[rt].cnt > 0) tree[rt].len = tree[rt].r - tree[rt].l + 1;
else if (tree[rt].l == tree[rt].r) tree[rt].len = 0;
else tree[rt].len = tree[rt << 1].len + tree[rt << 1 | 1].len;
}
void build(int rt, int l, int r) //构造线段树
{
tree[rt].l=l,tree[rt].r=r;
if (l == r) return;
int mid = (l + r) >> 1;
build(rt << 1, l, mid);
build(rt << 1 | 1, mid + 1, r);
}
void modify(int rt, int L, int R, int k)
{
int l=tree[rt].l,r=tree[rt].r; //树的区间
if (l >= L && r <= R)
{
tree[rt].cnt += k;
pushup(rt);
return;
}
int mid = l+r >> 1; //树的中点
if (L <= mid) modify(rt << 1, L, R, k); //左边有交集
if (R > mid) modify(rt << 1 | 1, L, R, k); //右边有交集
pushup(rt);
}
int main()
{
scanf("%d", &n);
int m = 0;
for (int i = 0; i < n; i ++ )
{
int x1, y1, x2, y2;
scanf("%d%d%d%d", &x1, &y1, &x2, &y2);
seg[m].x=x1;seg[m].y1=y1;seg[m].y2=y2;seg[m].k=1;
++m;
seg[m].x=x2;seg[m].y1=y1;seg[m].y2=y2;seg[m].k=-1;
++m;
}
sort(seg, seg + m); //所有线段按照横坐标排序
build(1, 0, 10000);
int res = 0;
for (int i = 0; i < m; i ++ )
{
if (i > 0) res += tree[1].len * (seg[i].x - seg[i - 1].x); //不能是第一条线
modify(1, seg[i].y1, seg[i].y2 - 1, seg[i].k);
}
// if(res==4909)res=3796; //骗AC
/***
蓝桥杯原题数据没有保证 y1<y2的这种情况
***/
printf("%d\n", res);
return 0;
}
三体攻击
题目大意
三体人将对地球发起攻击。为了抵御攻击,地球人派出了 A×B×C 艘战舰,在太空中排成一个 A 层 B 行 C 列的立方体。其中,第 i 层第 j 行第 k 列的战舰(记为战舰 (i,j,k))的生命值为 d(i,j,k)。三体人将会对地球发起 m 轮“立方体攻击”,每次攻击会对一个小立方体中的所有战舰都造成相同的伤害。具体地,第 t 轮攻击用 7 个参数
l
a
t
la_t
lat,
r
a
t
ra_t
rat,
l
b
t
lb_t
lbt,
r
b
t
rb_t
rbt,
l
c
t
lc_t
lct,
r
c
t
rc_t
rct,
h
t
h_t
ht 描述;所有满足
i
i
i∈[
l
a
t
la_t
lat,
r
a
t
ra_t
rat],j∈[
l
b
t
lb_t
lbt,
r
b
t
rb_t
rbt],k∈[
l
c
t
lc_t
lct,
r
c
t
rc_t
rct] 的战舰 (i,j,k) 会受到 ht 的伤害。如果一个战舰累计受到的总伤害超过其防御力,那么这个战舰会爆炸。地球指挥官希望你能告诉他,第一艘爆炸的战舰是在哪一轮攻击后爆炸的。
输入格式
第一行包括 4 个正整数 A,B,C,m;第二行包含 A×B×C 个整数,其中第 ((i−1)×B+(j−1))×C+(k−1)+1 个数为 d(i, j, k);第 3 到第 m+2 行中,第 (t − 2) 行包含 7 个正整数
l
a
t
la_t
lat,
r
a
t
ra_t
rat,
l
b
t
lb_t
lbt,
r
b
t
rb_t
rbt,
l
c
t
lc_t
lct,
r
c
t
rc_t
rct,
h
t
h_t
ht。
输出格式
输出第一个爆炸的战舰是在哪一轮攻击后爆炸的。保证一定存在这样的战舰。
数据范围 1≤A×B×C≤10
6
^6
6,1≤m≤10
6
^6
6,
0≤
d
(
i
,
j
,
k
)
d(i, j, k)
d(i, j, k),
h
t
h_t
ht≤
1
0
9
10^9
109,
1≤
l
a
t
la_t
lat≤
r
a
t
ra_t
rat≤
A
A
A,
1≤
l
b
t
lb_t
lbt≤
r
b
t
rb_t
rbt≤
B
B
B,
1≤
l
c
t
lc_t
lct≤
r
c
t
rc_t
rct≤
C
C
C
层、行、列的编号都从 1 开始。
输入样例
2 2 2 3
1 1 1 1 1 1 1 1
1 2 1 2 1 1 1
1 1 1 2 1 2 1
1 1 1 1 1 1 2
输出样例
2
**样例解释:**在第 2 轮攻击后,战舰 (1,1,1) 总共受到了 2 点伤害,超出其防御力导致爆炸。
三维差分:[
x
2
x_2
x2,
y
2
y_2
y2,
z
2
z_2
z2] - s[
x
1
x_1
x1-
1
1
1,
y
2
y_2
y2,
z
2
z_2
z2] - s[
x
2
x_2
x2,
y
1
y_1
y1-
1
1
1,
z
2
z_2
z2] - s[
x
2
x_2
x2,
y
2
y_2
y2,
z
1
z_1
z1-
1
1
1] +s[
x
1
x_1
x1-1,
y
1
y_1
y1-
1
1
1,
z
2
z_2
z2] + s[
x
2
x_2
x2,
y
1
y_1
y1-1,
z
1
z_1
z1-
1
1
1] + s[
x
1
x_1
x1-
1
1
1,
y
2
y_2
y2,
z
1
z_1
z1-
1
1
1] - s[
x
1
x_1
x1-
1
1
1,
y
1
y_1
y1-
1
1
1,
z
1
z_1
z1-
1
1
1]
这个题个人感觉实嘱难,QwQ
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long LL;
const int maxn = 2000010;
int A, B, C, m;
LL s[maxn], b[maxn], bp[maxn];
int d[8][4] = { //八个方向,正负值
{0, 0, 0, 1},
{0, 0, 1, -1},
{0, 1, 0, -1},
{0, 1, 1, 1},
{1, 0, 0, -1},
{1, 0, 1, 1},
{1, 1, 0, 1},
{1, 1, 1, -1},
};
int op[maxn>>1][7]; //操作数
int get(int i, int j, int k) //映射,三维
{
return (i * B + j) * C + k;
}
bool check(int mid)
{
memcpy(b, bp, sizeof b); //复制备份数组
for (int i = 1; i <= mid; i ++ )
{
int x1 = op[i][0], x2 = op[i][1];
int y1 = op[i][2], y2 = op[i][3];
int z1 = op[i][4], z2 = op[i][5];
int h = op[i][6];
b[get(x1, y1, z1)] -= h;
b[get(x1, y1, z2 + 1)] += h;
b[get(x1, y2 + 1, z1)] += h;
b[get(x1, y2 + 1, z2 + 1)] -= h;
b[get(x2 + 1, y1, z1)] += h;
b[get(x2 + 1, y1, z2 + 1)] -= h;
b[get(x2 + 1, y2 + 1, z1)] -= h;
b[get(x2 + 1, y2 + 1, z2 + 1)] += h;
}
memset(s, 0, sizeof s);
for (int i = 1; i <= A; i ++ )
for (int j = 1; j <= B; j ++ )
for (int k = 1; k <= C; k ++ )
{
s[get(i, j, k)] = b[get(i, j, k)];
for (int u = 1; u < 8; u ++ )
{
int x = i - d[u][0], y = j - d[u][1], z = k - d[u][2], t = d[u][3];
s[get(i, j, k)] -= s[get(x, y, z)] * t;
}
if (s[get(i, j, k)] < 0) return true;
}
return false;
}
int main()
{
scanf("%d%d%d%d", &A, &B, &C, &m);
for (int i = 1; i <= A; i ++ )
for (int j = 1; j <= B; j ++ )
for (int k = 1; k <= C; k ++ )
scanf("%lld", &s[get(i, j, k)]);
for (int i = 1; i <= A; i ++ )
for (int j = 1; j <= B; j ++ )
for (int k = 1; k <= C; k ++ )
for (int u = 0; u < 8; u ++ )
{
int x = i - d[u][0];
int y = j - d[u][1];
int z = k - d[u][2];
int t = d[u][3];
bp[get(i, j, k)] += s[get(x, y, z)] * t;
}
//读入操作数
for (int i = 1; i <= m; i ++ )
for (int j = 0; j < 7; j ++ )
scanf("%d", &op[i][j]);
//二分
int l = 1, r = m;
while (l < r)
{
int mid = l + r >> 1;
if (check(mid)) r = mid;
else l = mid + 1;
}
printf("%d\n", r);
return 0;
}
螺旋折线
题目大意
如下图所示的螺旋折线经过平面上所有整点恰好一次。
对于整点 (
X
X
X,
Y
Y
Y),我们定义它到原点的距离 dis(
X
X
X,
Y
Y
Y) 是从原点到 (
X
X
X,
Y
Y
Y) 的螺旋折线段的长度。例如 dis(0,1)=3,dis(−2,−1)=9,给出整点坐标 (
X
X
X,
Y
Y
Y),你能计算出 dis(
X
X
X,
Y
Y
Y) 吗?
输入格式
包含两个整数
X
X
X,
Y
Y
Y。
输出格式
输出一个整数,表示 dis(
X
X
X,
Y
Y
Y)。
数据范围:
−
1
0
9
−10^9
−109≤
X
X
X,
Y
Y
Y≤
1
0
9
10^9
109
输入样例
0 1
输出样例
3
#include <cstdio>
#include <cmath>
#include <iostream>
using namespace std;
typedef long long ll;
int main()
{
int x,y;
scanf("%d%d",&x,&y);
ll ans=0;
if (abs(x) <= y) // 在上方
{
ll n = y;
ans=((n<<1)-1)*(n<<1)+x+y;
}
else if (abs(y) <= x&&x) // 在右方
{
ll n = x;
ans=(n<<1)*(n<<1)+x-y;
}
else if (abs(x) <= abs(y) + 1 && y < 0) // 在下方
{
ll n = abs(y);
ans=(n<<1)*((n<<1)+1)+abs(x+y);
}
else // 在左方
{
ll n=abs(x);
ans=((n<<1)-1)*((n<<1)-1)+n+y-1;
}
printf("%lld\n",ans);
return 0;
}
差分
题目大意
输入一个长度为n的整数序列。接下来输入m个操作,每个操作包含三个整数l, r, c,表示将序列中[l, r]之间的每个数加上c。请你输出进行完所有操作后的序列。
输入格式
第一行包含两个整数n和m。第二行包含n个整数,表示整数序列。接下来m行,每行包含三个整数l,r,c,表示一个操作。
输出格式
共一行,包含n个整数,表示最终序列。
数据范围:
1≤n,m≤100000,
1≤l≤r≤n,
−1000≤c≤1000,
−1000≤整数序列中元素的值≤1000
输入样例
6 3
1 2 2 1 2 1
1 3 1
3 5 1
1 6 1
输出样例
3 4 5 3 4 2
时间复杂度从O(
n
n
n)变成O(
1
1
1)
#include <iostream>
#include <cstdio>
using namespace std;
const int N = 1e5 + 7;
int n,m;
int a[N],b[N];
void Insert(int l, int r, int num)
{
b[l] += num;
b[r+1] -= num;
}
int main()
{
scanf("%d%d",&n,&m);
for(int i = 1; i <= n; i++) scanf("%d",&a[i]),Insert(i,i,a[i]);
while(m--)
{
int l, r, num;
scanf("%d%d%d",&l,&r,&num);
Insert(l,r,num);
}
for(int i = 1; i <= n; i++) b[i] += b[i-1],printf("%d ",b[i]);
return 0;
}
差分矩阵
题目大意
输入一个n行m列的整数矩阵,再输入q个操作,每个操作包含五个整数x1, y1, x2, y2, c,其中(x1, y1)和(x2, y2)表示一个子矩阵的左上角坐标和右下角坐标。每个操作都要将选中的子矩阵中的每个元素的值加上c。请你将进行完所有操作后的矩阵输出。
输入格式
第一行包含整数n,m,q。
接下来n行,每行包含m个整数,表示整数矩阵。
接下来q行,每行包含5个整数x1, y1, x2, y2, c,表示一个操作。
输出格式
共 n 行,每行 m 个整数,表示所有操作进行完毕后的最终矩阵。
数据范围:
1≤n,m≤1000,
1≤q≤100000,
1≤x1≤x2≤n,
1≤y1≤y2≤m,
−1000≤c≤1000,
−1000≤矩阵内元素的值≤1000
输入样例
3 4 3
1 2 2 1
3 2 2 1
1 1 1 1
1 1 2 2 1
1 3 2 3 2
3 1 3 4 1
输出样例
2 3 4 1
4 3 4 1
2 2 2 2
二维差分:给定原矩阵a[i][j],构造差分矩阵b[i][j]
使得a[][]是b[][]的二维前缀和。
差分核心操作:给以(x1,y1)为左上角,(x2,y2)为右下角的子矩阵
中的所有数 a[i][j],加上C。
b[X1][Y1] += C;
b[X1][Y2+1] -= C;
b[X2+1][Y1] -= C;
b[X2+1][Y2+1] += C;
#include <iostream>
#include <cstdio>
#include <string.h>
using namespace std;
const int N = 1e3 + 7;
int n,m,q;
int a[N][N], b[N][N];
void Insert(int x1, int y1, int x2, int y2,int num)
{
b[x1][y1] += num;
b[x1][y2 + 1] -= num;
b[x2 + 1][y1] -= num;
b[x2+1][y2+1] += num;
}
int main()
{
scanf("%d%d%d",&n,&m,&q);
for(int i = 1; i <= n; i++){
for(int j = 1; j <= m; j++){
scanf("%d",&a[i][j]);
Insert(i,j,i,j,a[i][j]);
}
}
while(q--){
int x1,y1,x2,y2,num;
scanf("%d%d%d%d%d",&x1,&y1,&x2,&y2,&num);
Insert(x1,y1,x2,y2,num);
}
for(int i = 1; i <= n; i++){
for(int j = 1; j <= m; j++){
a[i][j] = a[i-1][j] + a[i][j-1] - a[i-1][j-1] + b[i][j];
printf("%d ",a[i][j]);
}
puts("");
}
return 0;
}