Astatinear
2022-07-26 08:07:18
同步于
前置芝士:
lca
。(用于树上差分。)如有错误,欢迎各位大佬指出。(顺便复习一下远古算法。)
我们先给定一个数组
那么如何构建一个普通的差分数组呢?
不难想到,我们假定
首先,我们先引入一个例题 P2367 语文成绩。题目意思大概就是说先给你一个序列,然后在进行区间加,最后求得区间的最小值即可。
而对于这种区间加减的操作,正是差分能够大展拳脚的地方。
我们先维护一个差分数组
最后,处理完这些询问之后,我们在最后求一次前缀和即可。
可以发现,差分修改操作时间复杂度为
最后贴上一份代码:
#include<bits/stdc++.h>
using namespace std;
int n,q;
int a[5000005],c[5000005];
int main()
{
scanf("%d%d",&n,&q);
for(int i=1;i<=n;++i) scanf("%d",&a[i]);
while(q--)
{
int l,r,x;
scanf("%d%d%d",&l,&r,&x);
c[l]+=x,c[r+1]-=x;//由于是差分,将c[l]+x,但为了抵消掉它对后面的贡献,我们将 c[r+1]-x
}
int minn=INT_MAX;
for(int i=1;i<=n;++i)
{
c[i]+=c[i-1];//求前缀和
a[i]+=c[i];//原数组记得加上
minn=min(minn,a[i]);//最小值
}
cout<<minn<<endl;
}
我们上述的,全都是一个序列(一维)的情况,但是,差分仍然可以扩展到二维。(对于其定义,与一维相类似,这里不做过多赘述)
假设我们当前修改的二维区间为
下面配上一个图方便理解。(图略丑,勿喷。)
假设我们要区间加
大概就是:
void chafen(int x1,int y1,int x2,int y2,int x)
{
b[x1][y1]+=x;
b[x1][y2+1]-=x;
b[x2+1][y1]-=x;
b[x1+1][y2+1]+=x;
}
最后还是给一个比较简单的例题吧。P3717 [AHOI2017初中组]cover,虽然可以不用二维差分做,但当一个练习的板子题还是挺好的。
这是一种非常常考也非常实用的一种差分形式。
我们先给出一种最基本的树上差分形式。即我们现在要完成的是将
然后我们给出树上差分的定义,即我们假设第
然后,我们来处理树上差分。首先,我们可以把
核心代码:
void tree(int x,int y,int z)
{
b[x]+=z,b[y]+=z;
b[lca(x,y)]-=z;b[fa[lca(x,y)]]-=z;
}
最后再奉上一个比较好想的例题:P6869 [COCI2019-2020#5] Putovanje
虽然在维护序列时,我们完全可以用线段树树状数组等一列数据结构来代替差分这种查询较慢的结构,但差分终究还是一个好写好想的算法,是不容易出错的。毕竟,如果考场上你在面临树上路径的操作时,不会树上差分,打一个码量极大还容易错的的树链剖分就太吃亏了。