7th 2022/6/10 树链剖分

开头的话

终于理解了!!!

树链剖分,算法如其名,就是:把——树——剖——成——几——条——链

链,是重链,即按子节点子树中节点最多的分

这里的“几”字,可不是随便用的

而是真的,只有\(\log n\)条!!!

emmm,所以在理解这个前,我一直认为树链剖分非常暴力

但……

有艺术的算法

这个算法的大概框架了解一下先

就是剖成多条链,然后标号后,同一条链上的节点编号也是连续的

连续???没错,线段树&树状数组乱入

emmm这两个算法总结以后会写

但,作为RMQ的主力,他们俩完全能够胜任连续编号节点的处理

就这样。。。树剖结束了

总的来说就是分成3部分

  1. 初始化,树剖需要的各个数据,

    如:父亲节点,深度,子书节点个数,重儿子节点,重链的顶部,树剖后的编号,和这个编号所对应的原来编号

  2. RMQ的初始化

  3. 游戏开始,查询吧

    注意:若查询的两个节点在一条链上,便直接RMQ他们

    但若不在,那么两个结点便不断\(O(1)\)

    地跳到当前链的链顶,一边跳一边记录

    当然,要取两个点中,点的链顶较深的那个

    不然一个领先另一个一堆,后面还没找到LCA,就先跳到root了

    直到在同一条链(很好判断的,就是链顶相同,因为链互不相交)

    不用怕链很多,因为他们就\(\log n\)个啊

就是这样啦

注意多复习,毕竟人总是会忘记事情的嘛。。。

代码(给出线段树,应用广,此处为查询路径和)

#include<cstdio>
using namespace std;
const int N=3e4+1;
int n,m,root,*a[N],len[N],b[N];
int fa[N],deep[N],size[N],top[N],dfn[N],bh[N],big_son[N];
int bz[N],x[N],y[N],kind,s,t,ans,cnt;
int f[4*N];
void swap(int &a,int &b){
	int g=a;a=b;b=g;
}
void cut_into_tree(int x){
	bz[x]=1;
	for(int i=1;i<=a[x][0];i++){
		if(bz[a[x][i]]==1){
			a[x][i]=-1;
			continue;
		}
		cut_into_tree(a[x][i]);
	}
}
void dfs1(int x,int fath,int now_deep){
	fa[x]=fath;
	deep[x]=now_deep;
	for(int i=1;i<=a[x][0];i++){
		if(a[x][i]==-1){
			continue;
		}
		dfs1(a[x][i],x,now_deep+1);
		size[x]+=size[a[x][i]];
	}
	size[x]++;
}
void dfs2(int x,int head){
	int mx=-2147483647,mxi;
	dfn[x]=++cnt;
	top[x]=head;
	int sum=0;
    for(int i=1;i<=a[x][0];i++){
		if(a[x][i]==-1){
			continue;
		}
		sum++;
		if(size[a[x][i]]>mx){
			mx=size[a[x][i]];
			mxi=i;
		}
	}
	if(sum>0){
		big_son[x]=mxi;
		dfs2(a[x][mxi],head);
		for(int i=1;i<=a[x][0];i++){
			if(a[x][i]==-1||i==mxi){
				continue;
			}
			dfs2(a[x][i],a[x][i]);
		}
	}
}
void build(int l,int r,int s){
	if(l==r){
		f[s]=b[bh[l]];
		return;
	}
	int mid=(l+r)/2;
	build(l,mid,s*2);
	build(mid+1,r,s*2+1);
	f[s]=f[s*2]+f[s*2+1];
}
void change(int l,int r,int s,int x,int y){
	if(l<=dfn[x]&&dfn[x]<=r){
		if(l==r){
			f[s]=y;
			return;
		}
		int mid=(l+r)/2;
		change(l,mid,s*2,x,y);
		change(mid+1,r,s*2+1,x,y);
		f[s]=f[s*2]+f[s*2+1];
	}
}
int query(int l,int r,int s,int x,int y){
	if(y<l||r<x){
		return 0;
	}
	if(x<=l&&r<=y){
		return f[s];
	}
	if(l==r){
		return 0;
	}
	int mid=(l+r)/2;
	return query(l,mid,s*2,x,y)+query(mid+1,r,s*2+1,x,y);
}
int get_link(int s,int t){
	ans=0;
	while(top[s]!=top[t]){
		if(deep[top[s]]>deep[top[t]]){
			ans+=query(1,n,1,dfn[top[s]],dfn[s]);
			s=fa[top[s]];
		}
		else{
			ans+=query(1,n,1,dfn[top[t]],dfn[t]);
			t=fa[top[t]];
		}
	}
	if(deep[s]>deep[t]){
		swap(s,t);
	}
	ans+=query(1,n,1,dfn[s],dfn[t]);
	return ans;
}
int main(){
	scanf("%d%d",&n,&m);
	root=1;
	for(int i=1;i<n;i++){
		scanf("%d%d",&x[i],&y[i]);
		len[x[i]]++;
		len[y[i]]++;
	}
	for(int i=1;i<=n;i++){
		a[i]=new int[len[i]+1];
		a[i][0]=0;
	}
	for(int i=1;i<n;i++){
		a[x[i]][++a[x[i]][0]]=y[i];
		a[y[i]][++a[y[i]][0]]=x[i];
	}
	for(int i=1;i<=n;i++){
		scanf("%d",&b[i]);
	}
	cut_into_tree(root);
	dfs1(root,-1,1);
	dfs2(root,root);
	for(int i=1;i<=n;i++){
		bh[dfn[i]]=i;
	}
	build(1,n,1); 
	for(int i=1;i<=m;i++){
		scanf("%d%d%d",&kind,&s,&t);
		if(kind==1){
			change(1,n,1,s,t);
		}
		else{
			printf("%d\n",get_link(s,t));
		}
	}
}
posted @ 2022-09-23 19:41  Far_delivery  阅读(29)  评论(0)    收藏  举报