图片来源于B站董晓老师,思路来源自《算法竞赛》
差分主要分为一维差分和二维差分,差分的应用重要在于推导
1.一维差分(P3372 【模板】线段树 1)
省略推导过程,精髓在于维护两颗树状数组 (d代表差分,query代表区间和)
贴代码
#include<iostream>
using namespace std;
int n,m;
int lowbit(int x){ //不必多言
return x & -x;
}
const int N=1e5+50;
using ll= long long;//这里不开ll要炸
ll tree1[N],tree2[N];//开两棵树维护
void update1(int x,ll k){ //简单的update,套模板就行
while(x<=n){
tree1[x]+=k;
x+=lowbit(x);
}
}
void update2(int x,ll k){
while(x<=n){
tree2[x]+=k;
x+=lowbit(x);
}
}
ll sum1(int x){ //求和,模板
ll ans=0;
while(x>0){
ans+=tree1[x];
x-=lowbit(x);
}
return ans;
}
ll sum2(int x){
ll ans=0;
while(x>0){
ans+=tree2[x];
x-=lowbit(x);
}
return ans;
}
int main(){
cin>>n>>m;
for(int i=1;i<=n;++i){//一个一个创造差分数组(初始化)
ll h;cin>>h;
update1(i,h);update1(i+1,-h);
//这里需要注意,维护的树状数组会跟着点的变化而变化,所以前后得到的不同(i和i+1)
update2(i,h*(i-1));update2(i+1,-h*i);
}
int op,x,y;
while(m--){
cin>>op>>x>>y;
if(op==1){
ll k;cin>>k;
update1(x,k);update1(y+1,-k);
update2(x,k*(x-1));update2(y+1,-k*y);
//这里前后不同原因如上
}
else{
cout<<sum1(y)*y-sum2(y)-(sum1(x-1)*(x-1)-sum2(x-1))<<endl;
//上面推导的公式代进去
}
}
return 0;
}
2.二维差分(P4514 上帝造题的七分钟)
前置知识(二维差分)

推导如下:
如上图所示,我们需要维护四个树状数组,下面用代码解释
#include<iostream>
using namespace std;
const int N=2050;
int tree1[N][N],tree2[N][N],tree3[N][N],tree4[N][N];//四个数组
int lowbit(int x){//不必多言
return x & (-x);
}
int n,m;char op;
void update(int a,int b,int x){ //更新
for(int i=a;i<=n;i+=lowbit(i)){
for(int j=b;j<=m;j+=lowbit(j)){
//这里在函数内部每个维护的数组不同,使用x而不是i的原因是外部的x本应在外部操作
//但是由于这里再内部统一进行操作,这样代码量更少,与我前面写的那一篇有所区别
tree1[i][j]+=x;tree2[i][j]+=x*a;
tree3[i][j]+=x*b;tree4[i][j]+=x*a*b;
}
}
}
int sum(int x,int y){
int ans=0;
for(int i=x;i>0;i-=lowbit(i)){
for(int j=y;j>0;j-=lowbit(j)){
//这里套了之前得到的结论公式
ans+=tree1[i][j]*(x+1)*(y+1)-tree2[i][j]*(y+1)-tree3[i][j]*(x+1)+tree4[i][j];
}
}
return ans;
}
int main(){
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
cin>>op>>n>>m;
while(cin>>op){
int a,b,c,d,e;
if(op=='L'){
cin>>a>>b>>c>>d>>e;//二维差分操作,四个点
update(a,b,e);update(c+1,d+1,e);
update(c+1,b,-e);update(a,d+1,-e);
}
else{
cin>>a>>b>>c>>d;
//这里使用了前缀和,每一个地方求导的sum都可以当作是这个点覆盖区域的面积
cout<<sum(c,d)-sum(c,b-1)-sum(a-1,d)+sum(a-1,b-1)<<endl;
}
}
return 0;
}
文章介绍了在一维和二维差分背景下,如何利用树状数组数据结构来高效地进行区间和的计算,并提供了两个实例,展示了差分数组的创建和更新操作以及它们在算法竞赛中的应用。
659

被折叠的 条评论
为什么被折叠?



