模板——树链剖分

这个树剖的代码写的我好绝望啊……两棵树傻傻搞不清。

写了两天的树剖

我脑子都快被剖开来了!!!

(超级想要引用这句话的,充分体现了我的绝望)

好,怒吼完毕,接下来来讲一下代码。(现在想想搞树剖也许是一个错误的决定,要不是因为某天天爱跑步我才不会来搞树剖呢)
其实这个树剖把……思想是挺简单的,但是主要是刚开始不知道思想的话看代码会一塌糊涂,所以先在这里把总体思想讲一下。

为了防止部分读者还不是很明白,这里给出几个名词的介绍

重儿子:指对于节点B的所有子节点中,若有x为B的子节点并且以x为根节点的子树的节点树在所有子节点中最多,则称x为B的重儿子。

轻儿子:不是重儿子的就是轻儿子。

重链:所有节点都是重儿子的就是重链。

轻链:所有节点都是轻儿子的就是轻链。

接下来我们讲思想。我们假设题目给出了一棵树A,然后有询问两点间的距离,以及将两点间最短路径的值进行修改(增加或减少)。我们可以先将重链轻链求出来,并且给A的每一个节点赋予一个新的编号,使得每一条重链上的节点的编号是连续的。这样子之后,我们就将一棵树A变成了一条链A,然后我们再造一颗线段树B,来维护A。对于每一次修改或者询问操作,我们都可以把它拆成一条条链,然后由于每一条链的编号都是连续的,就可以用B来进行区间操作,使得复杂度变低(虽然我不知道树剖的复杂度到底是什么)

#include<bits/stdc++.h>
using namespace std;
const int N=10000001;
//这里先声明一下,我们称树A原来的编码叫做编码A,后来我们重新赋值的编码称为编码B 
int son[N],deep[N],siz[N],id[N],val[N],top[N],fa[N],jump[N]={0},n,num;
//son[i]表示i(编码A)节点的重儿子的节点编号(编码A)
//deep[i]表示i(编码A)节点的深度
//siz[i]表示以i(编码A)为节点的子树的节点个数
//id[i]表示节点i(编码A)重新编号后的编号(编号B)
//val[i]表示节点i(编码B)的值
//top[i]表示节点i(编码A)这条重链的顶端节点编号(编码A)
//fa[i]表示节点i(编码A)的父节点的编号(编码A)
//jump是邻接表
int sum=0;
struct kob{
	int sta,ed,jump,val;
}a[2*N];
//这个是邻接表存储的边 
struct nob{
	int fa,son,val;
}edge[N];
//这个是刚开始存储的边 
struct tre{
	int l,r,val,mark;
}t[4*N];
//这个是树B
void pushdown(int root){
	t[root].val+=t[root].mark*(t[root].r-t[root].l+1);
	t[root<<1].mark+=t[root].mark;
	t[root<<1|1].mark+=t[root].mark;
	t[root].mark=0;
}
//树B的lazy标记 
void add(int sta,int ed,int val){
	sum++;
	a[sum].sta=sta;
	a[sum].ed=ed;
	a[sum].jump=jump[sta];
	jump[sta]=sum;
}
void dfs1(int root,int fath,int deepp){
	deep[root]=deepp;
	siz[root]=1;
	son[root]=0;
	fa[root]=fath;
	for (int i=jump[root]; i; i=a[i].jump){
		int pos=a[i].ed;
		if (pos==fath || pos==son[root]) continue;
		dfs1(pos,root,deepp+1);
		siz[root]+=siz[pos];
		if (siz[son[root]]<siz[pos])
			son[root]=pos;
	}
}
void dfs2(int root,int tp){
	top[root]=tp;
	id[root]=++num;
	if (son[root]) dfs2(son[root],tp);
	for (int i=jump[root]; i; i=a[i].jump){
		int pos=a[i].ed;
		if (pos==fa[root] || pos==son[root]) continue;
		dfs2(pos,pos);
	}
}
void Build(int root,int l,int r){
	t[root].l=l;
	t[root].r=r;
	if (l==r){
		t[root].val=val[l];
		return ;
	}
	int mid=(l+r)>>1;
	Build(root<<1,l,mid);
	Build(root<<1|1,mid+1,r);
	t[root].val=t[root<<1].val+t[root<<1|1].val;
}
int find(int root,int le,int ri){
	if (t[root].mark) pushdown(root);
	if (le<=t[root].l && t[root].r<=ri){
		return t[root].val;
	}
	int mid=(t[root].l+t[root].r)>>1;
	if (ri<=mid) return find(root<<1,le,ri);
	else if (le>mid) return find(root<<1|1,le,ri);
	else return find(root<<1,le,mid)+find(root<<1|1,mid+1,ri);
}
//find函数表示在树B上查询A的一段区间值 
int search(int root,int down,int up){
	int fu=top[up],fd=top[down],rem=0;
	while (fu!=fd){
		if (deep[fu]>deep[fd]){
			swap(fu,fd);
			swap(up,down);
		}
		rem+=find(1,id[fd],id[down]);
		down=fa[fd];
		fd=top[down];
	}
	if (down==up) return rem;//由于边是存储在他的子节点上的,所以这个时候应当返回
	if (deep[up]>deep[down]) swap(up,down);
	rem+=find(1,id[son[up]],id[down]);
	return rem;
}
//将两点中深度top较小的往上跳,然后每次跳一条链,由于跳的是一条链
//所以可以直接在树B上修改一段区间,当两个点的top相同时,两点肯定在一条链上
//所以可以直接修改两点之间的部分 
void ch(int root,int le,int ri,int val){
	if (t[root].mark) pushdown(root);
	if (le<=t[root].l && t[root].r<=ri){
		t[root].mark+=val;
		pushdown(root);
		return ;
	}
	int mid=(t[root].l+t[root].r)>>1;
	if (ri<=mid) ch(root<<1,le,ri,val);
	else if (le>mid) ch(root<<1|1,le,ri,val);
	else{
		ch(root<<1,le,mid,val);
		ch(root<<1|1,mid+1,ri,val);
	}
	t[root].val=t[root<<1].val+t[root<<1|1].val;
}
void change(int root,int down,int up,int val){
	int fu=top[up],fd=top[down];
	while (fu!=fd){
		if (deep[fu]>deep[fd]){
			swap(fu,fd);
			swap(up,down);
		}
		ch(1,id[fd],id[down],val);
		down=fa[fd];
		fd=top[down];
	}
	if (down==up) return ;
	if (deep[up]>deep[down]) swap(up,down);
	ch(1,id[son[up]],id[down],val);
}
//修改时也同理
int main(){
	scanf("%d",&n);
	for (int i=1; i<n; i++){
		scanf("%d%d%d",&edge[i].fa,&edge[i].son,&edge[i].val);
		add(edge[i].fa,edge[i].son,edge[i].val);
		add(edge[i].son,edge[i].fa,edge[i].val);
	}
	num=0;
	dfs1(1,0,1);
	//dfs1将树A上的deep,siz,son,fa数组求出 
	dfs2(1,1);
	//dfs2求出top以及为A重新编码,产生编码B 
	for (int i=1; i<n; i++){
		if (deep[edge[i].fa]>deep[edge[i].son]) swap(edge[i].fa,edge[i].son);
		val[id[edge[i].son]]=edge[i].val;
	}
	//将edge的边的父节点和子节点确定,同时求出val数组 
	Build(1,1,num);
	//建树B 
	string s;
	while(cin>>s && s[0]!='D'){
		int x,y,z;
		scanf("%d%d",&x,&y);
		if (s=="Question")
			printf("%d\n",search(1,x,y));
		else if (s=="Change"){
			scanf("%d",&z);
			change(1,x,y,z);
		}
	}
	return 0;
}
posted @ 2017-10-25 21:28  |斗蜂|  阅读(189)  评论(1编辑  收藏  举报