树链剖分1

具体见OI-wiki,下面是一些补充

重链要求是极大的

每个点都在某一个重链中,如果一个点是重子节点,那么其在与其父亲所连的边的重链中,否则在与其重子节点所连的边的重链中

image

这一段的原因:我们走重链是不用关心的,因为同一重链的dfs序是连续的,我们可以用其他数据结构维护,我们只用关心这条路径被划分成了多少条重链以及哪些重链,而重链一旦改变,肯定会经过轻边,也就是说最多划分成 O ( log ⁡ n ) O(\log n) O(logn)条重链

然后划分链的过程:由于重边属于唯一一条重链,所以我们直接从当前点往上走(沿着重边)走到当前重链的最上面的一个点(这条重链的顶点),然后统计这一段的答案,并循环即可,具体见代码

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn=1e5+10;
int n,w[maxn],q;
int cur,Last[maxn],Next[maxn<<1],End[maxn<<1];
int sz[maxn],tp[maxn],son[maxn],dep[maxn],fa[maxn],dfn[maxn],rk[maxn],cnt,maxdfn[maxn];
ll sum[maxn<<2],lazy[maxn<<2];
void add(int x,int y)
{
	End[++cur]=y,Next[cur]=Last[x],Last[x]=cur;
}
void dfs1(int x,int father)
{
	son[x]=-1;
	sz[x]=1;
	for(int i=Last[x];i;i=Next[i])
	{
		int u=End[i];
		if(u==father) continue;
		dep[u]=dep[x]+1;
		fa[u]=x;
		dfs1(u,x);
		sz[x]+=sz[u];
		if(son[x]==-1||sz[son[x]]<sz[u])
		son[x]=u;
	}
}
void dfs2(int x,int t)
{
	tp[x]=t;//tp[x]表示x所在重链的顶点 
	cnt++;
	dfn[x]=cnt;
	rk[cnt]=x;
	if(son[x]==-1) 
	{
		maxdfn[x]=cnt;
		return;
	}
	dfs2(son[x],t);//注意第二个参数是t 
	maxdfn[x]=maxdfn[son[x]];
	for(int i=Last[x];i;i=Next[i])
	{
		int u=End[i];
		if(u!=fa[x]&&u!=son[x]) 
		{
			dfs2(u,u);//注意两个参数都是u 
			maxdfn[x]=max(maxdfn[x],maxdfn[u]);
		}
	}
}
void build(int p,int l,int r)
{
	if(l==r)
	{
		sum[p]=w[rk[l]];
		return;
	}
	int mid=l+r>>1;
	build(p<<1,l,mid);
	build(p<<1|1,mid+1,r);
	sum[p]=sum[p<<1]+sum[p<<1|1];
}
void pushdown(int p,int l,int r)
{
	int mid=l+r>>1;
	lazy[p<<1]+=lazy[p],sum[p<<1]+=lazy[p]*(mid-l+1);
	lazy[p<<1|1]+=lazy[p],sum[p<<1|1]+=lazy[p]*(r-mid);
	lazy[p]=0;
}
void modify(int p,int l,int r,int x,int y,ll val)
{
	if(l>y||r<x) return;
	if(l>=x&&r<=y)
	{
		lazy[p]+=val;
		sum[p]+=(r-l+1)*val;
		return;
	}
	pushdown(p,l,r);
	int mid=l+r>>1;
	modify(p<<1,l,mid,x,y,val);
	modify(p<<1|1,mid+1,r,x,y,val);
	sum[p]=sum[p<<1]+sum[p<<1|1];
}
void modify1(int x,int y,ll val)
{
	int fx=tp[x],fy=tp[y];
  	while(fx!=fy) 
	{
    	if(dep[fx]>=dep[fy])
      	modify(1,1,n,dfn[fx],dfn[x],val),x=fa[fx];
    	else
      	modify(1,1,n,dfn[fy],dfn[y],val),y=fa[fy];
    	fx=tp[x];
    	fy=tp[y];
  	}
  	if(dfn[x]<dfn[y])
    modify(1,1,n,dfn[x],dfn[y],val);
  	else
    modify(1,1,n,dfn[y],dfn[x],val);
}
ll ask(int p,int l,int r,int x,int y)
{
	if(l>y||r<x) return 0;
	if(l>=x&&r<=y) return sum[p];
	pushdown(p,l,r);
	int mid=l+r>>1;
	return ask(p<<1,l,mid,x,y)+ask(p<<1|1,mid+1,r,x,y);
}
ll query1(int x,int y)
{
	ll res=0;
	int fx=tp[x],fy=tp[y];
  	while(fx!=fy) 
	{
    	if(dep[fx]>=dep[fy])
      	res+=ask(1,1,n,dfn[fx],dfn[x]),x=fa[fx];
    	else
      	res+=ask(1,1,n,dfn[fy],dfn[y]),y=fa[fy];
    	fx=tp[x];
    	fy=tp[y];
  	}
  	if(dfn[x]<dfn[y])
    res+=ask(1,1,n,dfn[x],dfn[y]);
  	else
    res+=ask(1,1,n,dfn[y],dfn[x]);
    return res;
}
int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
    scanf("%d",&w[i]);
    for(int i=1;i<n;i++)
    {
    	int a,b;
    	scanf("%d%d",&a,&b);
    	add(a,b),add(b,a);
	}
	dfs1(1,0);
	dfs2(1,1);//注意第二个参数是1 
	build(1,1,n);
	scanf("%d",&q);
	for(int i=1;i<=q;i++)
	{
		int op;
		scanf("%d",&op);
		if(op==1)
		{
			int u,v,k;
			scanf("%d%d%d",&u,&v,&k);
			modify1(u,v,k); 
		 } 
		else if(op==2)
		{
			int u,k;
			scanf("%d%d",&u,&k);
			modify(1,1,n,dfn[u],maxdfn[u],k);
		}
		else if(op==3)
		{
			int u,v;
			scanf("%d%d",&u,&v);
			printf("%lld\n",query1(u,v));
		}
		else
		{
			int u;
			scanf("%d",&u);
			printf("%lld\n",ask(1,1,n,dfn[u],maxdfn[u]));
		}
	}
    return 0;
}
  • 6
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值