树链剖分

树链剖分就是将树分割成多条链,然后利用数据结构(线段树、树状数组等)来维护这些链。
首先就是一些必须知道的概念:
重结点:子树结点数目最多的结点;
轻节点:父亲节点中除了重结点以外的结点;
重边:父亲结点和重结点连成的边;
轻边:父亲节点和轻节点连成的边;
重链:由多条重边连接而成的路径;
轻链:由多条轻边连接而成的路径;

在这里插入图片描述
比如上面这幅图中,用黑线连接的结点都是重结点,其余均是轻结点,2-11、1-11就是重链,其他就是轻链,用红点标记的就是该结点所在链的起点,也就是我们?提到的top结点,还有每条边的值其实是进行dfs时的执行序号。
数组:

int val[N];//树上点的权值
int siz[N];//以x为根的子节点数
int dep[N];//该点在树上的深度
int fa[N];//他的爸爸是谁
int top[N];//该点所在的重链的深度最浅的节点
int dfn[N];//dfs序 (重链在此数组上是连续的,以一个点为根的所有子节点在此也是连续的
int id[N];//把dfn对回树上编号
int son[N];//重儿子

流程

两遍dfs确定以上数组
第一次dfs,确定dep,fa,son,siz数组。

void dfs1(int u,int fa,int dist)
{
	dep[u]=dist;
	siz[u]=1;
	faz[u]=fa;
	for(int i=head[u];i;i=edge[i].nxt)
	{
		int v=edge[i].v;
		if(v==fa) continue;
		dfs1(v,u,dist+1);
		siz[u]+=siz[v];
		if(son[u]==-1||siz[v]>siz[son[u]]) son[u]=v;
	}
}

第二次dfs确定top,dfn(dfs序,树上的一个点在线段树上的编号),tid(tid[i]线段树上区间为为i的子节点在树上的dfs序)

void dfs2(int u,int t)
{
	top[u]=t;
	cc++;
	dfn[u]=cc;
	tid[cc]=u;
	if(!son[u]) return ;//如果已经是子节点了,return掉
	dfs2(son[u],t);//先沿重链搜下去
	for(int i=head[u];i;i=edge[i].nxt)
	{
		int v=edge[i].v;
		if(v!=son[u]&&v!=faz[u])//找轻链
		{
			dfs2(v,v);
		}
	}
}

可以完成的操作:

  1. 在x->y的路径上加K;
  2. 查询x->y的路径上的点权和
  3. 子树的和
  4. 子树全部加K
    操作一二相当于找x->y的路径,把这条路径上所有的点求和或加一个值,我们考虑求lca,top数组可以加速求lca的过程
    操作三四就更简单了,因为一棵子树的dfn值是连续的
void modify_path(int x,int y,int z)
{
	int fx=top[x],fy=top[y];
	while(fx!=fy)
	{
		if(dep[fx]>dep[fy])
		{
			modify(1,1,n,dfn[fx],dfn[x],z);
			x=faz[fx];
		}
		else
		{
			modify(1,1,n,dfn[fy],dfn[y],z);
			y=faz[fy];
		}
		fx=top[x];fy=top[y];
	}
	if(dfn[x]>dfn[y]) swap(x,y);
	modify(1,1,n,dfn[x],dfn[y],z);
}
int query_path(int x,int y)
{
	int res=0;
	int fx=top[x],fy=top[y];
	while(fx!=fy)
	{
		if(dep[fx]>dep[fy])
		{
			res=(res+query(1,1,n,dfn[fx],dfn[x]))%p;
			x=faz[fx];
		}
		else
		{
			res=(res+query(1,1,n,dfn[fy],dfn[y]))%p;
			y=faz[fy];
		}
		fx=top[x];fy=top[y];
	}
	if(dfn[x]>dfn[y]) swap(x,y);
	res=(res+query(1,1,n,dfn[x],dfn[y]))%p;
	return res;
}

思路

https://www.cnblogs.com/George1994/p/7821357.html

传送门

https://www.luogu.org/problemnew/show/P3384

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
#include<cmath>
#include<cstdlib>
#include<ctime>
using namespace std;
typedef long long ll;
const int inf=0x3f3f3f3f;
inline int read(){
	char ch=' ';int f=1;int x=0;
	while(ch<'0'||ch>'9'){if(ch=='-') f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();
	return x*f;
}
const int N=1e5+100;
int p,root;
int val[N];
struct node
{
	int v,nxt;
}edge[N<<1];
int head[N],cnt;
void add(int u,int v)
{
	cnt++;
	edge[cnt].v=v;
	edge[cnt].nxt=head[u];
	head[u]=cnt;
}
int dep[N],siz[N],faz[N],son[N];
void dfs1(int u,int fa,int dist)
{
	dep[u]=dist;
	siz[u]=1;
	faz[u]=fa;
	for(int i=head[u];i;i=edge[i].nxt)
	{
		int v=edge[i].v;
		if(v==fa) continue;
		dfs1(v,u,dist+1);
		siz[u]+=siz[v];
		if(!son[u]||siz[v]>siz[son[u]]) son[u]=v;
	}
}
int top[N],dfn[N],tid[N],cc=0;
void dfs2(int u,int t)
{
	top[u]=t;
	cc++;
	dfn[u]=cc;
	tid[cc]=u;
	if(!son[u]) return ;
	dfs2(son[u],t);
	for(int i=head[u];i;i=edge[i].nxt)
	{
		int v=edge[i].v;
		if(v!=son[u]&&v!=faz[u])
		{
			dfs2(v,v);
		}
	}
}
int sum[N<<2];
int tag[N<<2];
int n;
void update(int rt)
{
	sum[rt]=sum[rt<<1]+sum[rt<<1|1];
}
void build(int rt,int l,int r)
{
	if(l==r)
	{sum[rt]=val[tid[l]];return ;}
	int mid=(l+r) >> 1;
	build(rt<<1,l,mid);
	build(rt<<1|1,mid+1,r);
	update(rt);
}
void pushdown(int rt,int l,int r)
{
	if(tag[rt])
	{
		int k=tag[rt],mid=(l+r)>>1;tag[rt]=0;
		sum[rt<<1]=(sum[rt<<1]+(mid-l+1)*k)%p;
		tag[rt<<1]=(tag[rt<<1]+k)%p;
		sum[rt<<1|1]=(sum[rt<<1|1]+(r-mid)*k)%p;
		tag[rt<<1|1]=(tag[rt<<1|1]+k)%p;
	}
}
void modify(int rt,int l,int r,int x,int y,int z)
{
	if(l>=x&&r<=y)
	{
		sum[rt]=(sum[rt]+(r-l+1)*z)%p;
		tag[rt]=(tag[rt]+z)%p;
		return ;
	}
	pushdown(rt,l,r);
	int mid=(l+r) >> 1;
	if(x<=mid)
	{
		modify(rt<<1,l,mid,x,y,z);
	}
	if(y>mid)
	{
		modify(rt<<1|1,mid+1,r,x,y,z);
	}
	update(rt);
}
int query(int rt,int l,int r,int x,int y)
{
	if(l>=x&&r<=y)
	{
		return sum[rt];
	}
	pushdown(rt,l,r);
	int mid=(l+r) >> 1;
	int res=0;
	if(x<=mid)
	{
		res=(res+query(rt<<1,l,mid,x,y))%p;
	}
	if(y>mid)
	{
		res=(res+query(rt<<1|1,mid+1,r,x,y))%p;
	}
	return res;
}
void modify_path(int x,int y,int z)
{
	int fx=top[x],fy=top[y];
	while(fx!=fy)
	{
		if(dep[fx]>dep[fy])
		{
			modify(1,1,n,dfn[fx],dfn[x],z);
			x=faz[fx];
		}
		else
		{
			modify(1,1,n,dfn[fy],dfn[y],z);
			y=faz[fy];
		}
		fx=top[x];fy=top[y];
	}
	if(dfn[x]>dfn[y]) swap(x,y);
	modify(1,1,n,dfn[x],dfn[y],z);
}
int query_path(int x,int y)
{
	int res=0;
	int fx=top[x],fy=top[y];
	while(fx!=fy)
	{
		if(dep[fx]>dep[fy])
		{
			res=(res+query(1,1,n,dfn[fx],dfn[x]))%p;
			x=faz[fx];
		}
		else
		{
			res=(res+query(1,1,n,dfn[fy],dfn[y]))%p;
			y=faz[fy];
		}
		fx=top[x];fy=top[y];
	}
	if(dfn[x]>dfn[y]) swap(x,y);
	res=(res+query(1,1,n,dfn[x],dfn[y]))%p;
	return res;
}
int main()
{
	int m;
	n=read();m=read();root=read();p=read();
	int i,j;
	for(i=1;i<=n;i++) val[i]=read();
	int u,v;
	for(i=1;i<n;i++){u=read();v=read();add(u,v);add(v,u);}
	dfs1(root,0,1);
	dfs2(root,root);
	build(1,1,n);
	int op,x,y,z;
	for(i=1;i<=m;i++)
	{
		op=read();
		if(op==1)
		{
			x=read();y=read();z=read();
			modify_path(x,y,z);
		}
		else if(op==2)
		{
			x=read();y=read();
			printf("%d\n",query_path(x,y));
		}
		else if(op==3)
		{
			int x=read(),z=read();
			modify(1,1,n,dfn[x],dfn[x]+siz[x]-1,z);
		}
		else
		{
			int x=read();
			printf("%d\n",query(1,1,n,dfn[x],dfn[x]+siz[x]-1));
		}
	}
	return 0;
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值