一维/二维线性前缀和&差分

一维/二维线性前缀和&差分

线性前缀和差分是解决数据结构——区间的最基本的工具,能够高效的解决区间修改,单点查询。除了线性前缀和&差分,还有树上前缀和&差分。

一维线性前缀和&差分

没啥好说的,不说了。

二维线性前缀和&差分

设原数组为 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[i1][j],b[i][j1],b[i1][j1]。发现:

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[i1][j]a[i][j1]+a[i1][j1]

这就是二维差分公式。反推得二维前缀和公式:

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[i1][j]+a[i][j1]a[i1][j1]

例题

P3397

如果对一个二维区域做差分,对这个二维区域(橙色)整体加 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数组,如果将数组某段连续翻转,我们可以进行对其进行异或差分,修改边界元素即可。

P5057

#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;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值