树链剖分 边权下放点权

Grass Planting G

大意:

就那么些操作,没有大意

思路:
这里就简单讲一下如何实现

首先,对于一条边,它有两个顶点,那么肯定是让深度较浅的点,也就是父节点的点权来作为这条边的边权。然后最后我们要查边权的时候,只要查它对应的子节点就可以了

那么我们为了实现该操作,在原本的板子上,有三点要进行改变:

1.dfs时更新点权,也就是让边权赋给子节点

for(int i=head[id];i!=-1;i=edge[i].next)
	{
		ll y=edge[i].t;
		if(y==p) continue;
		a[y]=edge[i].l; //如果初始边权不为0,点权下放边权时要执行这一句操作 
		init_dfs(y,id,depth+1);
		siz[id]+=siz[y];
		if(siz[y]>son_val)
		{
			son_val=siz[y];
			son[id]=y;
		}
	}	

2.树上路径更新的时候,你稍微手玩一下就会发现两个节点的lca是不应该被更新的,所以在最后一步更新的时候,我们的代码要有所改变

ll tree_add(ll x,ll y,ll val)
{
	while(top[x]!=top[y])
	{
		if(dep[top[x]]<dep[top[y]]) swap(x,y);
		add(1,idx[top[x]],idx[x],val); 
		x=fa[top[x]];
	}
	if(dep[x]>dep[y]) swap(x,y);
	add(1,idx[x]+1,idx[y],val);//lca不查询,所以x要+1
}

当然你也可以这么写

ll tree_add(ll x,ll y,ll val)
{
	while(top[x]!=top[y])
	{
		if(dep[top[x]]<dep[top[y]]) swap(x,y);
		add(1,idx[top[x]],idx[x],val); 
		x=fa[top[x]];
	}
	if(dep[x]>dep[y]) swap(x,y);
	add(1,idx[x],idx[y],val);
    add(1,idx[x],idx[x],-val);
}

其实就是把lca的更新的结果给改回去

3.查询树上路径权值和时(如果有这个操作),同理两点的lca的点权不应该被算进去,因为它对应的边不属于这两点的路径上。

ll tree_sum(ll x,ll y)
{
	ll ans=0;
	while(top[x]!=top[y])
	{
		if(dep[top[x]]<dep[top[y]]) swap(x,y);
		ans+=sum(1,idx[top[x]],idx[x]);
		x=fa[top[x]];
	}
	if(dep[x]>dep[y]) swap(x,y);
	ans+=sum(1,idx[x]+1,idx[y]);//点权下放边权操作核心代码,lca不查询,所以x要+1 
	return ans;
}

同理,你也可以这么写,意思我就不解释了

ll tree_sum(ll x,ll y)
{
	ll ans=0;
	while(top[x]!=top[y])
	{
		if(dep[top[x]]<dep[top[y]]) swap(x,y);
		ans+=sum(1,idx[top[x]],idx[x]);
		x=fa[top[x]];
	}
	if(dep[x]>dep[y]) swap(x,y);
	ans+=sum(1,idx[x],idx[y]);
    ans-=sum(1,idx[x],idx[x]);
	return ans;
}

会了这个操作之后这题你就毫无问题了

不过按惯例还是贴一下code

#include<bits/stdc++.h>
using namespace std;
#define ll int
#define endl '\n'
const ll N=1e5+10;
struct ty{
	ll t,l,next;
}edge[N<<1];
ll cn=0;
ll n,k;
char op;
ll aa,b,c,rt;
ll head[N];
void add_edge(ll a,ll b,ll c)
{
	edge[++cn].t=b;
	edge[cn].l=c;
	edge[cn].next=head[a];
	head[a]=cn;
}
struct tree
{
	ll l,r;
	ll val;
	ll add;//lazy
	ll siz;
}tr[N<<2];
ll dep[N];//深度
ll fa[N],son[N];
ll siz[N];
ll mas[N],a[N];//初始序列,处理后的序列
ll cnt=0;//dfs序的时间戳
ll top[N];//所在链的顶端
ll idx[N];
void init_dfs(ll id,ll p,ll depth)
{
    dep[id]=depth;
    fa[id]=p;
	siz[id]=1;
	ll son_val=0;
	for(int i=head[id];i!=-1;i=edge[i].next)
	{
		ll y=edge[i].t;
		if(y==p) continue;
		//a[y]=edge[i].l; 如果初始边权不为0,点权下放边权时要执行这一句操作 
		init_dfs(y,id,depth+1);
		siz[id]+=siz[y];
		if(siz[y]>son_val)
		{
			son_val=siz[y];
			son[id]=y;
		}
	}		
} 
void dfs2(ll id,ll p)
{//更新链上的情况 
	idx[id]=++cnt;
	a[cnt]=mas[id];
	top[id]=p;
	if(!son[id]) return;
	dfs2(son[id],p);
	for(int i=head[id];i!=-1;i=edge[i].next)
	{
		ll y=edge[i].t;
		//if(y==p||y==son[id]) continue;
		if(!idx[y]) dfs2(y,y);//新的链 
	}
}
void push_up(ll p)
{
	tr[p].val+=tr[p<<1].val+tr[p<<1|1].val;
}
void build(ll p,ll l,ll r)
{
	tr[p].l=l;tr[p].r=r;tr[p].siz=r-l+1;
	if(l==r)
	{
		tr[p].val=a[l];
		return;
	}
	ll mid=l+r>>1;
	build(p<<1,l,mid);
	build(p<<1|1,mid+1,r);
}
void push_down(ll p)
{
	if(tr[p].add==0) return;
	tr[p<<1].val+=tr[p<<1].siz*tr[p].add;
	tr[p<<1|1].val+=tr[p<<1|1].siz*tr[p].add;
	tr[p<<1].add+=tr[p].add;
	tr[p<<1|1].add+=tr[p].add;
	tr[p].add=0;
}
void add(ll p,ll l,ll r,ll val)
{
	if(tr[p].l>=l&&tr[p].r<=r)
	{
		tr[p].val+=tr[p].siz*val;
		tr[p].add+=val;
		return;
	}
	push_down(p);
	ll mid=tr[p].l+tr[p].r>>1;
	if(l<=mid) add(p<<1,l,r,val);
	if(r>mid) add(p<<1|1,l,r,val);
	push_up(p);
}
ll sum(ll p,ll l,ll r)
{
	ll ans=0;
	if(tr[p].l>=l&&tr[p].r<=r)
	{
		return tr[p].val;
	}
	push_down(p);
	ll mid=tr[p].l+tr[p].r>>1;
	if(l<=mid) ans+=sum(p<<1,l,r);
	if(r>mid) ans+=sum(p<<1|1,l,r);
	return ans;
}
ll tree_sum(ll x,ll y)
{
	ll ans=0;
	while(top[x]!=top[y])
	{
		if(dep[top[x]]<dep[top[y]]) swap(x,y);
		ans+=sum(1,idx[top[x]],idx[x]);
		x=fa[top[x]];
	}
	if(dep[x]>dep[y]) swap(x,y);
	ans+=sum(1,idx[x]+1,idx[y]);//点权下放边权操作核心代码,lca不查询,所以y要+1 
	return ans;
}
ll tree_add(ll x,ll y,ll val)
{
	while(top[x]!=top[y])
	{
		if(dep[top[x]]<dep[top[y]]) swap(x,y);
		add(1,idx[top[x]],idx[x],val); 
		x=fa[top[x]];
	}
	if(dep[x]>dep[y]) swap(x,y);
	add(1,idx[x]+1,idx[y],val);//同上一句注释 
}
void trson_add(ll x,ll val)
{
	add(1,idx[x],idx[x]+siz[x]-1,val);
}
ll trson_sum(ll x)
{
	return sum(1,idx[x],idx[x]+siz[x]-1);
}
void solve()
{
	cin>>n>>k;
	for(int i=1;i<n;++i)
	{
		cin>>aa>>b;
		add_edge(aa,b,0);
		add_edge(b,aa,0);
	}
	init_dfs(rt,0,1);
	dfs2(rt,rt);
	build(1,1,n);
	while(k--)
	{
		cin>>op>>aa>>b;
		if(op=='P')
		{
			tree_add(aa,b,1);
		}
		else cout<<tree_sum(aa,b)<<endl;
	}
}
int main()
{ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
	memset(head,-1,sizeof head);
	rt=1;
	solve();
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值