BZOJ 2836 魔法树 树链剖分

本文介绍了一道关于树剖和线段树维护的算法题解,通过使用树剖和线段树来高效地处理节点间的更新和查询操作。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Description

Input

Output

Sample Input

4
0 1
1 2
2 3
4
Add 1 3 1
Query 0
Query 1
Query 2

Sample Output

3
3
2

HINT






今天考试考了去年day 1的题……只会100+40+60的暴力。
渣死了,心情不好。

看到有一题树剖裸题……那就随手写了。。。
数组要大点,线段树维护有毒。



#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int 
	N=150005;
int n,Ecnt,cnt;
struct Edge{
	int next,to;
}E[N<<1];int head[N];
struct Tree{
	int pre,top,tid,deep,sz,son,maxid;
}tree[N];
struct Segment{
	ll mark,sum;
}seg[N<<2];
void add(int u,int v){
	E[++Ecnt].next=head[u];
	E[Ecnt].to=v;
	head[u]=Ecnt;
}
void up(int u){
	seg[u].sum=seg[u<<1].sum+seg[u<<1|1].sum;
}
void down(int u,int L,int R){
	if (u && seg[u].mark){
		int l=u<<1,r=u<<1|1;
		int mid=(L+R)>>1;
		seg[l].mark+=seg[u].mark;
		seg[r].mark+=seg[u].mark;
		seg[l].sum+=seg[u].mark*(ll)(mid-L+1);
		seg[r].sum+=seg[u].mark*(ll)(R-mid);
		seg[u].mark=0LL;
	}
}
void dfs1(int u,int pre,int depth){
	tree[u].pre=pre;
	tree[u].sz=1;
	tree[u].deep=depth;
	tree[u].son=0;
	int maxs=0;
	for (int i=head[u];i;i=E[i].next){
		int j=E[i].to;
		if (j==pre) continue;
		dfs1(j,u,depth+1);
		tree[u].sz+=tree[j].sz;
		if (maxs<tree[j].sz)
			maxs=tree[j].sz,tree[u].son=j;
	}
}
void dfs2(int u,int ancestor){
	tree[u].maxid=tree[u].tid=++cnt;
	tree[u].top=ancestor;
	if (tree[u].son) dfs2(tree[u].son,ancestor);
	tree[u].maxid=max(tree[u].maxid,tree[tree[u].son].maxid);
	for (int i=head[u];i;i=E[i].next){
		int j=E[i].to;
		if (j==tree[u].pre || j==tree[u].son) continue;
		dfs2(j,j);
		tree[u].maxid=max(tree[u].maxid,tree[j].maxid);
	}
}
void update(int id,int l,int r,int gl,int gr,ll num){
	down(id,l,r);
	if (l>=gl && r<=gr){
		seg[id].mark+=num;
		seg[id].sum+=(ll)(r-l+1)*num;
		return;
	}
	int mid=(l+r)>>1;
	if (gl<=mid) update(id<<1,l,mid,gl,gr,num);
	if (gr>mid) update(id<<1|1,mid+1,r,gl,gr,num);
	up(id);
}
ll query(int id,int l,int r,int gl,int gr){
	down(id,l,r);
	if (l>=gl && r<=gr) return seg[id].sum;
	int mid=(l+r)>>1;
	ll t=0LL;
	if (gl<=mid) t+=query(id<<1,l,mid,gl,gr);
	if (gr>mid) t+=query(id<<1|1,mid+1,r,gl,gr);
	return t;
}
void change(int u,int v,ll d){
	while (tree[u].top!=tree[v].top){
		if (tree[tree[u].top].deep<tree[tree[v].top].deep) swap(u,v);
		update(1,1,n,tree[tree[u].top].tid,tree[u].tid,d);
		u=tree[tree[u].top].pre;
	}
	if (tree[u].deep<tree[v].deep) swap(u,v);
	update(1,1,n,tree[v].tid,tree[u].tid,d);
}
ll querysum(int u){
	return query(1,1,n,tree[u].tid,tree[u].maxid);
}
int main(){
	cin>>n;
	int x,y;Ecnt=0;
	for (int i=1;i<n;i++){
		scanf("%d%d",&x,&y);
		x++,y++;
		add(x,y),add(y,x);
	}
	dfs1(1,-1,0);
	cnt=0,dfs2(1,1);
	int m;cin>>m;
	ll d;char s[10];
	while (m--){
		scanf("%s",s);
		if (s[0]=='A'){
			scanf("%d%d%lld",&x,&y,&d);
			x++,y++;
			change(x,y,d);
		} else{
			scanf("%d",&x);x++;
			printf("%lld\n",querysum(x));
		}
	}
	return 0;
}




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值