差分
解决的问题:差分可以以 O ( 1 ) O(1) O(1)的时间复杂度,对区间或者区域中的所有数进行 + C +C +C 或者 − C -C −C 的操作, C C C 为任意常数
题目一:差分
题目描述
输入一个长度为
n
n
n 的整数序列。
接下来输入
m
m
m 个操作,每个操作包含三个整数
l
,
r
,
c
l,r,c
l,r,c,表示将序列中 [l,r] 之间的每个数加上
c
c
c
。
请你输出进行完所有操作后的序列。
输入格式
第一行包含两个整数 n n n 和 m m m。
第二行包含 n n n 个整数,表示整数序列。
接下来 m m m 行,每行包含三个整数 l , r , c l,r,c l,r,c,表示一个操作。
输出格式
共一行,包含 n n n 个整数,表示最终序列。
数据范围
1
≤
n
,
m
≤
100000
1≤n,m≤100000
1≤n,m≤100000,
1
≤
l
≤
r
≤
n
1≤l≤r≤n
1≤l≤r≤n,
−
1000
≤
c
≤
1000
−1000≤c≤1000
−1000≤c≤1000,
−
1000
≤
整数序列中元素的值
≤
1000
−1000≤整数序列中元素的值≤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
思路
本题为差分的模板题,差分与前缀和互为逆运算。
差分数组进行前缀和运算可以得到原数组,前缀和数组进行差分运算可以得到原数组。
差分数组的使用:给定一个差分数组b,我们要使得原数组区间
[
l
,
r
]
[l, r]
[l,r] 中的数都
+
c
+c
+c,相当于
b[l] += c
b[r + 1] -= c
在计算前缀和得到原数组时,我们发现
a
[
l
]
a[l]
a[l] ~
a
[
r
]
a[r]
a[r]中的所有数都加上了
C
C
C
代码
#include <iostream>
using namespace std;
const int N = 100010;
int q[N], b[N], n, m;
int main()
{
cin >> n >> m;
for (int i = 1; i <= n; i ++)
{
int x;
cin >> x;
b[i] += x;
b[i + 1] -= x;
}
while (m --)
{
int l, r, c;
cin >> l >> r >> c;
b[l] += c;
b[r + 1] -= c;
}
for (int i = 1; i <= n; i ++)
{
b[i] += b[i - 1];
cout << b[i] << ' ';
}
cout << endl;
return 0;
}
题目二:差分矩阵
题目描述
输入一个
n
n
n 行
m
m
m 列的整数矩阵,再输入
q
q
q 个操作,每个操作包含五个整数
x
1
,
y
1
,
x
2
,
y
2
,
c
x_1,y_1,x_2,y_2,c
x1,y1,x2,y2,c,其中
(
x
1
,
y
1
)
(x_1,y_1)
(x1,y1) 和
(
x
2
,
y
2
)
(x_2,y_2)
(x2,y2) 表示一个子矩阵的左上角坐标和右下角坐标。
每个操作都要将选中的子矩阵中的每个元素的值加上
c
c
c。
请你将进行完所有操作后的矩阵输出。
输入格式
第一行包含整数 n , m , q n,m,q n,m,q。
接下来
n
n
n 行,每行包含
m
m
m 个整数,表示整数矩阵。
接下来
q
q
q 行,每行包含
5
5
5 个整数
x
1
,
y
1
,
x
2
,
y
2
,
c
x_1,y_1,x_2,y_2,c
x1,y1,x2,y2,c,表示一个操作。
输出格式
共 n n n 行,每行 m m m 个整数,表示所有操作进行完毕后的最终矩阵。
数据范围
1
≤
n
,
m
≤
1000
1≤n,m≤1000
1≤n,m≤1000,
1
≤
q
≤
100000
1≤q≤100000
1≤q≤100000,
1
≤
x
1
≤
x
2
≤
n
1≤x1≤x2≤n
1≤x1≤x2≤n,
1
≤
y
1
≤
y
2
≤
m
1≤y1≤y2≤m
1≤y1≤y2≤m,
−
1000
≤
c
≤
1000
−1000≤c≤1000
−1000≤c≤1000,
−
1000
≤
矩阵内元素的值
≤
1000
−1000≤矩阵内元素的值≤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[x1][y1] += c;
a[x1][y2 + 1] -= c;
a[x2 + 1][y1] -= c;
a[x2 + 1][y2 + 1] += c;
不过多赘述,感兴趣参考前缀和的形式进行画图
代码
#include <iostream>
using namespace std;
const int N = 1010;
int a[N][N], b[N][N];
int n, m, q;
int main()
{
cin >> n >> m >> q;
for (int i = 1; i <= n; i ++)
for (int j = 1; j <= m; j ++)
{
int c;
cin >> c;
a[i][j] += c;
a[i][j + 1] -= c;
a[i + 1][j] -= c;
a[i + 1][j + 1] += c;
}
while (q --)
{
int x1, y1, x2, y2, c;
cin >> x1 >> y1 >> x2 >> y2 >> c;
a[x1][y1] += c;
a[x1][y2 + 1] -= c;
a[x2 + 1][y1] -= c;
a[x2 + 1][y2 + 1] += c;
}
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];
for (int i = 1; i <= n; i ++)
{
for (int j = 1; j <= m; j ++)
cout << a[i][j] << ' ';
cout << endl;
}
return 0;
}
题目三 IncDec Sequence(增减序列)
题目描述
给定一个长度为
n
n
n 的数列
a
1
,
a
2
,
…
,
a
n
a_1,a_2,…,a_n
a1,a2,…,an,每次可以选择一个区间
[
l
,
r
]
[l,r]
[l,r],使下标在这个区间内的数都加一或者都减一。
求至少需要多少次操作才能使数列中的所有数都一样,并求出在保证最少次数的前提下,最终得到的数列可能有多少种。
输入格式
第一行输入正整数 n n n。
接下来 n n n 行,每行输入一个整数,第 i + 1 i+1 i+1 行的整数代表 a i a_i ai。
输出格式
第一行输出最少操作次数。
第二行输出最终能得到多少种结果。
数据范围
0
<
n
≤
1
0
5
0<n≤10^5
0<n≤105 ,
0
≤
a
i
<
2147483648
0≤a_i<2147483648
0≤ai<2147483648
输入样例:
4
1
1
2
2
输出样例:
1
2
思考
代码
#include <iostream>
using namespace std;
typedef long long LL;
const int N = 100010;
int b[N];
int n;
int main()
{
cin >> n;
for (int i = 1; i <= n; i ++)
{
int x;
cin >> x;
b[i] += x;
b[i + 1] -= x;
}
LL p = 0, q = 0;
for (int i = 2; i <= n; i ++)
{
if (b[i] > 0) p += b[i];
if (b[i] < 0) q += b[i];
}
q = abs(q);
cout << min(p, q) + abs(p - q) << endl;
cout << abs(p - q) + 1 << endl;
return 0;
}
题目四:改变数组元素
题目描述
给定一个空数组 V V V 和一个整数数组 a 1 , a 2 , … , a n a_1,a_2,…,a_n a1,a2,…,an。
现在要对数组 V V V 进行 n n n 次操作。
第
i
i
i 次操作的具体流程如下:
1.从数组
V
V
V 尾部插入整数
0
0
0。
2.将位于数组
V
V
V 末尾的
a
i
a_i
ai 个元素都变为
1
1
1(已经是 1 的不予理会)。
注意:
a
i
a_i
ai 可能为
0
0
0,即不做任何改变。
a
i
a_i
ai 可能大于目前数组
V
V
V 所包含的元素个数,此时视为将数组内所有元素变为
1
1
1。
请你输出所有操作完成后的数组 V V V。
输入格式
第一行包含整数 T T T,表示共有 T T T 组测试数据。
每组数据第一行包含整数 n n n。
第二行包含 n n n 个整数 a 1 , a 2 , … , a n a_1,a_2,…,a_n a1,a2,…,an。
输出格式
每组数据输出一行结果,表示所有操作完成后的数组 V V V,数组内元素之间用空格隔开。
数据范围
1
≤
T
≤
20000
1≤T≤20000
1≤T≤20000,
1
≤
n
≤
2
×
105
1≤n≤2×105
1≤n≤2×105,
0
≤
a
i
≤
n
0≤a_i≤n
0≤ai≤n,
保证一个测试点内所有
n
n
n 的和不超过
2
×
1
0
5
2×10^5
2×105。
输入样例:
3
6
0 3 0 0 1 3
10
0 0 0 1 0 5 0 0 0 2
3
0 0 0
输出样例:
1 1 0 1 1 1
0 1 1 1 1 1 0 0 1 1
0 0 0
思路
开数组记录空数组中,每个位置的操作次数,次数大于等于1的位置为1,否则为0
使用差分数组记录区间操作次数(某区间中的所有数设为1记为该区间的所有位置进行一次操作)
#include <iostream>
#include <cstring>
using namespace std;
const int N = 200010;
int b[N], n, T;
int a[N];
int main()
{
cin >> T;
while (T -- )
{
cin >> n;
memset(a, 0, sizeof a);
for (int i = 1; i <= n; i ++)
cin >> b[i];
for (int i = 1; i <= n; i ++)
{
a[i - min(b[i], i) + 1] += 1;
a[i + 1] -= 1;
}
for (int i = 1; i <= n; i ++)
a[i] += a[i - 1];
for (int i = 1; i <= n; i ++)
{
if (a[i] >= 1) cout << "1 ";
else cout << "0 ";
}
cout << endl;
}
return 0;
}
完