zoukankan      html  css  js  c++  java
  • 模板——树链剖分

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

    写了两天的树剖

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

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

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

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

    重儿子:指对于节点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
    ",search(1,x,y));
    		else if (s=="Change"){
    			scanf("%d",&z);
    			change(1,x,y,z);
    		}
    	}
    	return 0;
    }
    
  • 相关阅读:
    如何成为合格的技术面试官?
    互联网上有多少个网站?
    前端领域不需要架构师?
    WebAssembly 简介
    git常用命令
    剑指offer-基本思想学习(未包括代码)
    OS知识点总结
    对软件工程的一点认识
    项目实现过程的每个阶段
    编译原理课程设计总结
  • 原文地址:https://www.cnblogs.com/cain-/p/7732473.html
Copyright © 2011-2022 走看看