树状数组中差分应用(P3372 线段树、P4514上帝造题的七分钟)

图片来源于B站董晓老师,思路来源自《算法竞赛》

差分主要分为一维差分和二维差分,差分的应用重要在于推导

1.一维差分(P3372 【模板】线段树 1)

省略推导过程,精髓在于维护两颗树状数组 (d代表差分,query代表区间和)

树状数组中差分应用(P3372 线段树、P4514上帝造题的七分钟)_第1张图片贴代码

#include
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<

2.二维差分(P4514 上帝造题的七分钟)

前置知识(二维差分)

树状数组中差分应用(P3372 线段树、P4514上帝造题的七分钟)_第2张图片

推导如下:树状数组中差分应用(P3372 线段树、P4514上帝造题的七分钟)_第3张图片如上图所示,我们需要维护四个树状数组,下面用代码解释

#include
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<

你可能感兴趣的:(算法,c++,数据结构)