差分及前缀和

差分及前缀和
  • 一维前缀和(略

    P3131

  • 二维前缀和

    预处理:

    s u m x , y = ∑ i = 1 x ∑ j = 1 y a i , j sum_{x,y} = \sum_{i = 1}^{x}\sum_{j = 1}^{y}a_{i,j} sumx,y=i=1xj=1yai,j

    s u m [ x ] [ y ] = s u m [ x ] [ y − 1 ] + s u m [ x − 1 ] [ y ] − s u m [ x − 1 ] [ y − 1 ] + a [ x ] [ y ] sum[x][y] = sum[x][y - 1] + sum[x - 1][y] - sum[x - 1][y - 1] + a[x][y] sum[x][y]=sum[x][y1]+sum[x1][y]sum[x1][y1]+a[x][y]

​ 查询:

​ 求 ( x 1 , y 1 ) − ( x 2 , y 2 ) (x_1,y_1) - (x_2,y_2) (x1,y1)(x2,y2) 子矩阵的和

s u m [ x 2 ] [ y 2 ] − s u m [ x 1 − 1 ] [ y 2 ] − s u m [ x 2 ] [ y 1 − 1 ] + s u m [ x 1 − 1 ] [ y 1 − 1 ] sum[x_2][y_2] - sum[x_1 - 1][y_2] - sum[x_2][y_1 - 1] + sum[x_1 - 1][y_1 - 1] sum[x2][y2]sum[x11][y2]sum[x2][y11]+sum[x11][y11]

​ 题目:

P1387

const int N = 105;
int a[N][N];
int sum[N][N];
int main ()
{
	//freopen("input.in", "r", stdin);
	//freopen("test.out", "w", stdout);
	int n, m;
	read(n);
	read(m);
	for (int i = 1; i <= n; i++)
	{
		for (int j = 1; j <= m; j++)
			read(a[i][j]);
	}
	for (int i = 1; i <= n; i++)
	{
		for (int j = 1; j <= m; j++)
			sum[i][j] = sum[i - 1][j] + sum[i][j - 1] - sum[i - 1][j - 1] + a[i][j];
	}
	int ans = 0;
	for (int d = 1; d <= min(n, m); d++) {
		for (int i = 1; i + d - 1 <= n; i++)
		{
			for (int j = 1; j + d - 1 <= m; j++)
			{
                 int x = i + d - 1;
                 int y = j + d - 1;
                 int _ = sum[x][y] - sum[x][j - 1] - sum[i - 1][y] + sum[i - 1][j - 1];
                 if(_ == d * d)
                  ans = max(ans,d);  
			}
		}
	}
	cout << ans << endl;
	return 0 ;
}

CodeVs1373

  • 树上前缀和

​ 设 s u m i sum_i sumi 表示节点 i i i 到根节点的权值总和。

​ 若是点权, x , y x,y x,y 路径上的和为 s u m x + s u m y − s u m l c a − s u m f a l c a sum_x + sum_y - sum_{lca} - sum_{fa_{lca}} sumx+sumysumlcasumfalca

​ 若是边权, x , y x,y x,y 路径上的和为 s u m x + s u m y − 2 × s u m l c a sum_x + sum_y - 2 \times sum_{lca} sumx+sumy2×sumlca

l c a lca lca :最近公共祖先

LOJ2491

  • 差分

    b i = a i − a i − 1 b_i = a_i - a_{i -1} bi=aiai1 即相邻两数的差

    易知: b i b_i bi 的前缀和是 a i a_i ai

​ 一维差分:区间操作 [ l , r ] [l,r] [l,r] 区间加 k k k ,做法是 b l + k , b r + 1 − k b_l+k,b_{r+1} - k bl+k,br+1k 最后再前缀和做一下就可以求得 a i + = k &ThinSpace; ( l ≤ i ≤ r ) a_i += k \,(l\leq i\leq r) ai+=k(lir) 的结果。

​ 或者 b i = 0 b_i = 0 bi=0 , b l + k , b r + 1 − k b_l + k,b_{r+1} - k bl+k,br+1k 之后取前缀和即可。

​ 二维差分:方法是和一维类似的,我们也是需要另开一个数组记录修改操作,最后求前缀和时统计修改操作,只是二维每一次操作需要记录4个位置,一维只需要记录2个位置。最后对 b [ i ] [ j ] b[i][j] b[i][j] 求二维前缀和,再 a [ i ] [ j ] a[i][j] a[i][j] 中相加即可。

for(int i=0;i<m;i++){//m是修改操作次数 
	int x1,y1,x2,y2,p;
	cin>>x1>>y1>>x2>>y2>>p;
	b[x1][y1]+=p;b[x2+1][y2+1]+=p;
	b[x2+1][y1]-=p;b[x1][y2+1]-=p
}
  • 树上差分

    • 树的性质:

    1 、 1、 1 树上任意两个点的路径唯一

    2 、 2、 2任何子节点的父亲节点唯一(根节点没有父亲)

    • 点的差分:

    s → t s \rightarrow t st 求这条路径上点被经过的次数

    c n t i cnt_i cnti 表示节点 i i i 被经过的次数

    ​ 做法: c n t s + + cnt_s++ cnts++ , c n t t + + cnt_t++ cntt++ , c n t l c a − − cnt_{lca}-- cntlca, c n t f a t h e r ( l c a ) − − cnt_{father(lca)}-- cntfather(lca)

    ​ 考虑搜索到 s s s ,再从 s s s 回溯到 l c a lca lca 时, c n t l c a = 0 cnt_{lca} = 0 cntlca=0

    ​ 搜索到 t t t ,再从 t t t 回溯到 l c a lca lca ,此时 c n t l c a = 1 cnt_{lca} = 1 cntlca=1

    ​ 还有一个问题:如果从 l c a lca lca 点向上回溯时会使得父亲节点子树和为 1 1 1 ,所以需要在 c n t f a t h e r ( l c a ) − − cnt_{father(lca)}-- cntfather(lca).

    • 边的差分:

    ​ 边的差分是把:边权赋到点上(常用套路,点通常时边所连的儿子节点。

    c n t s + + , c n t t + + , c n t l c a − = 2 cnt_s++,cnt_t++,cnt_{lca}-=2 cnts++,cntt++,cntlca=2

    ​ 边权到儿子节点,在 d f s dfs dfs 中加上即可。

  • 参考博客:

大佬

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值