树上差分注意点

1.点上记录值为相对于子节点的差分,子树差分和为根的和
2.求LCA有多种算法,选择其中一种倍增算法

#include<bits/stdc++.h>
#define pb push_back
const int N = 1e6 + 5;
using namespace std;
typedef long long ll;
#define debug(x) cout<<#x<<" is "<<x<<endl
#define eprintf(...) fprintf(stderr, __VA_ARGS__)
int n,m,R;
vector<int> g[N];
int vv[N];
int tot = 0;
int l[N],r[N],fa[N][20];
inline int read()
{
	int X=0; bool flag=1; char ch=getchar();
	while(ch<'0'||ch>'9') {if(ch=='-') flag=0; ch=getchar();}
	while(ch>='0'&&ch<='9') {X=(X<<1)+(X<<3)+ch-'0'; ch=getchar();}
	if(flag) return X;
	return ~(X-1);
}
inline void write(ll X)
{
	if(X<0) {X=~(X-1); putchar('-');}
	if(X>9) write(X/10);
	putchar(X%10+'0');
}
int deep[N];
void dfs(int u,int pre,int dep){
	deep[u] = dep;
	l[u] = ++tot;
	for(int i = 0;i < g[u].size();i++){
		int v = g[u][i];
		if(v == pre)continue;
		dfs(v,u,dep + 1);
		fa[v][0] = u;

	}
	r[u] = tot;
} 
void init(){
    for(int j=0;(1<<(j+1))<n;j++)
    for(int i=1;i<=n;i++)
    {
        if(fa[i][j]<=0) fa[i][j+1]=0;
        else fa[i][j+1]=fa[fa[i][j]][j];
    }
}

int lca(int u,int v)
{
    if(deep[u]>deep[v]) swap(u,v);
    int temp=deep[v]-deep[u];
    for(int i=0;(1<<i)<=temp;i++)
    {
        if((1<<i)&temp) v=fa[v][i];
    }
    if(v==u) return u;
    for(int i=(int)(log(n*1.0)/log(2.0));i>=0;i--)
    {
        if(fa[u][i]!=fa[v][i])
        {
            u=fa[u][i];
            v=fa[v][i];
        }
    }
    return fa[u][0];
}
struct BIT{
	ll a[N];
	int n;
	void init(int nn){
		n = nn;
		for(int i = 1;i <= n;i++)a[i] = 0;
	}
	int lowbit(int x){return x&(-x);}
	
	void update(int i,ll x){
		if(i == 0)return;
		while(i <= n){
			a[i] += x;
			i += lowbit(i);
		}
	}
	ll query(int i){
		ll ans = 0;
		while(i > 0){
			ans += a[i];
			i -= lowbit(i);
		}
		return ans;
	}
}t1,t2;
void update(int u,int v,ll val){
	if(u != v){
		int ca = lca(u,v);
		t1.update(l[u],val);
		t1.update(l[v],val);
		t1.update(l[ca],-val);
		t1.update(l[fa[ca][0]],-val);
		t2.update(l[u],val * deep[u]);
		t2.update(l[v],val * deep[v]);
		t2.update(l[ca],- val * deep[ca]); 
		t2.update(l[fa[ca][0]],-val * deep[fa[ca][0]]);
	}else{
		t1.update(l[u],val);
		t1.update(l[fa[u][0]],-val);
		
		t2.update(l[u],val * deep[u]);
		t2.update(l[fa[u][0]],-val * deep[fa[u][0]]);
	}
}

void solve(){
	n = read();m = read(),R = read();
	t1.init(n);t2.init(n);
	for(int i = 1;i <= n;i++)
		vv[i] = read();
	for(int i = 1;i <= n - 1;i++){
		int u,v;
		u = read(),v = read();
		g[u].pb(v);
		g[v].pb(u);
	}
	dfs(R,-1,1);
	init();
	for(int i = 1;i <= n;i++){
		update(i,i,vv[i]);
	}
	while(m--){
		int op,x,y,z;
		op = read();
		if(op == 1){
			x = read(),y = read(),z = read();
			update(x,y,z);
		}else {
			x = read();
			ll ans;
			if(op == 2)ans = t1.query(r[x]) - t1.query(l[x] - 1);
			else {
				ll tt1 = t1.query(r[x]) - t1.query(l[x] - 1);
				ll tt2 = t2.query(r[x]) - t2.query(l[x] - 1);
				ans = tt2 - tt1 * (deep[x] - 1);
			}
			write(ans);
			puts("");
		}
	}
}
inline void fastIO(){
	ios::sync_with_stdio(false);
	cin.tie(0);
	cout.tie(0);
}
int main(){
	//fastIO();
	solve();
	return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值