一维/二维线性前缀和&差分
线性前缀和差分是解决数据结构——区间的最基本的工具,能够高效的解决区间修改,单点查询。除了线性前缀和&差分,还有树上前缀和&差分。
一维线性前缀和&差分
没啥好说的,不说了。
二维线性前缀和&差分
设原数组为 a [ i ] [ j ] a[i][j] a[i][j],差分数组为 b [ i ] [ j ] b[i][j] b[i][j],那么根据一维差分的思想:原数组是差分数组的前缀和,我们可以推出:
a [ i ] [ j ] = b [ 1 ] [ 1 ] + b [ 1 ] [ 2 ] + … + b [ 1 ] [ j ] + b [ 2 ] [ 1 ] + b [ 2 ] [ 2 ] + … + b [ 2 ] [ j ] + … + b [ i ] [ 1 ] + b [ i ] [ 2 ] + … + b [ i ] [ j ] a[i][j] = b[1][1] + b[1][2] + \ldots + b[1][j] \\ +b[2][1] + b[2][2] + \ldots + b[2][j] \\ +\ldots +b[i][1] + b[i][2] + \ldots + b[i][j] a[i][j]=b[1][1]+b[1][2]+…+b[1][j]+b[2][1]+b[2][2]+…+b[2][j]+…+b[i][1]+b[i][2]+…+b[i][j]
移项得到 b [ i ] [ j ] = … b[i][j]=\ldots b[i][j]=…,同理推出 b [ i − 1 ] [ j ] , b [ i ] [ j − 1 ] , b [ i − 1 ] [ j − 1 ] b[i-1][j],b[i][j-1],b[i-1][j-1] b[i−1][j],b[i][j−1],b[i−1][j−1]。发现:
b [ i ] [ j ] = a [ i ] [ j ] − a [ i − 1 ] [ j ] − a [ i ] [ j − 1 ] + a [ i − 1 ] [ j − 1 ] b[i][j] = a[i][j] - a[i-1][j]-a[i][j-1]+a[i-1][j-1] b[i][j]=a[i][j]−a[i−1][j]−a[i][j−1]+a[i−1][j−1]
这就是二维差分公式。反推得二维前缀和公式:
a [ i ] [ j ] = b [ i ] [ j ] + a [ i − 1 ] [ j ] + a [ i ] [ j − 1 ] − a [ i − 1 ] [ j − 1 ] a[i][j] = b[i][j] + a[i-1][j] + a[i][j-1] - a[i-1][j-1] a[i][j]=b[i][j]+a[i−1][j]+a[i][j−1]−a[i−1][j−1]
例题
如果对一个二维区域做差分,对这个二维区域(橙色)整体加 1 1 1,发现差分数组 b b b的变化为:
#include <bits/stdc++.h>
using namespace std;
#define FR freopen("in.txt","r",stdin)
typedef long long ll;
int diff[1005][1005];
int n,m;
int main()
{
cin >> n >> m;
while(m--)
{
int x1,y1,x2,y2;
cin >>y1 >> x1 >> y2 >> x2;
diff[y1][x1]++;
diff[y1][x2+1]--;
diff[y2+1][x1]--;
diff[y2+1][x2+1]++;
}
for(int r = 1; r<=n; r++)
{
for(int c = 1; c<=n; c++)
{
diff[r][c] += diff[r][c-1] + diff[r-1][c] - diff[r-1][c-1];
cout << diff[r][c] << " ";
}
cout << endl;
}
return 0;
}
异或前缀和、差分
如果把加法换成异或运算,我们维护一个01数组,如果将数组某段连续翻转,我们可以进行对其进行异或差分,修改边界元素即可。
#include <cstdio>
int N, M, B[100001];
inline void A(int i) { for (; i <= N; i += i & -i) B[i] ^= 1; }
inline int Q(int i) { int A = 0; for (; i; i -= i & -i) A ^= B[i]; return A; }
int main() {
scanf("%d%d", &N, &M);
while (M--) {
int opt, l, r;
scanf("%d%d", &opt, &l);
if (opt == 1) A(l), scanf("%d", &r), A(r + 1);
else printf("%d\n", Q(l));
}
return 0;
}