差分技巧学习指南
前置芝士
二维差分数组
\(1≤q≤100000,1≤n,m≤10^3,1≤x1≤x2≤n,1≤y1≤y2≤m,1≤a_{i,j},c≤10^5\)
void solve(){
int n,m,q;
cin>>n>>m>>q;
vector<vector<ll>> a(n+1,vector<ll>(m+1,0));
vector<vector<ll>> b(n+2,vector<ll>(m+2,0));
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++) cin>>a[i][j];
while(q--){
int x0,y0,x1,y1,c;
cin>>x0>>y0>>x1>>y1>>c;
// cout<<x0<<y0<<x1<<y1<<c<<endl;
//下标从1开始
b[x0][y0]+=c;
b[x0][y1+1]-=c;
b[x1+1][y0]-=c;
b[x1+1][y1+1]+=c;
}
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
b[i][j]+=b[i][j-1]+b[i-1][j]-b[i-1][j-1];
cout<<b[i][j]+a[i][j]<<" ";
}
cout<<endl;
}
}
三维差分数组
树上差分
要求对树上的一段路径进行操作,并询问某个点或某条边被经过的次数,树上差分就可以派上用场了。
对路径上的重要节点进行修改(而不是暴力全改),作为其差分数组的值,最后在求值时,利用dfs遍历求出差分数组的前缀和,就可以达到降低复杂度的目的。
点差分
设将两点u,v之间路径上的所有点权增加x,o=LCA(u,v),o的父亲节点为p,则操作如下:
diff[u]+=x,diff[v]+=x,diff[o]-=x,diff[p]-=x;
现要将2,3之间路径上的所有点的权值增加3,设原权值均为0。
边差分
将两点u,v之间路径上的所有边权增加x,o=LCA(u,v),以每条边两端深度较大的节点存储该边的差分数组
diff[u]+=x,diff[v]+=x,diff[o]-=2*x;