• 浅谈树链剖分


    Ⅰ、预备知识

    树链剖分,又叫重链剖分,树剖。顾名思义,就是在树上将树划分为一条条链,然后进行树上修改与查询操作(针对于结点操作,边权操作后面会讲),用数据结构来维护保证时间复杂度(数据结构模板),本篇文章中以线段树为例说明。一般来说,可以支持以下几种操作:
    1、树上路径区间修改
    2、树上路径间的区间查询(如点权之和,最大值等)
    讲道理实际上线段树能做的它在树上都能做QAQ
    接下来进入毒瘤内容

    Ⅱ、抛出问题

    先看一道板子

    题目描述

    如题,已知一棵包含N个结点的树(连通且无环),每个节点上包含一个数值,需要支持以下操作:
    操作1:格式:1 x y z 表示将树从x到y结点最短路径上所有节点的值都加上z
    操作2:格式:2 x y 表示求树从x到y结点最短路径上所有节点的值之和
    操作3:格式:3 x z 表示将以x为根节点的子树内所有节点值都加上z
    操作4:格式:4 x 表示求以x为根节点的子树内所有节点值之和

    输入输出格式

    输入格式:

    第一行包含4个正整数N、M、R、P,分别表示树的结点个数、操作个数、根节点序号和取模数(即所有的输出结果均对此取模)。
    接下来一行包含N个非负整数,分别依次表示各个节点上初始的数值。
    接下来N-1行每行包含两个整数x、y,表示点x和点y之间连有一条边(保证无环且连通)
    接下来M行每行包含若干个正整数,每行表示一个操作,格式如下:
    操作1: 1 x y z
    操作2: 2 x y
    操作3: 3 x z
    操作4: 4 x

    输出格式:

    输出包含若干行,分别依次表示每个操作2或操作4所得的结果(对P取模)

    输入输出样例

    输入样例#1:

    5 5 2 24
    7 3 7 8 0
    1 2
    1 5
    3 1
    4 1
    3 4 2
    3 2 2
    4 5
    1 5 1 3
    2 1 3

    输出样例#1:

    2
    21

    说明:

    时空限制:1s,128M
    数据规模:
    对于30%的数据:(Nleq 10,Mleq 10)
    对于70%的数据:(Nleq{10}^3,Mleq{10}^3)
    对于100%的数据:(Nleq{10}^5,Mleq{10}^5)
    (其实,纯随机生成的树LCA+暴力是能过的,可是,你觉得可能是纯随机的么233)
    样例说明:
    树的结构如下:

    各个操作如下:

    故输出应依次为2、21(重要的事情说三遍:记得取模)

    Ⅲ、分析问题

    先把几个概念弄明白:

    sz[u] 子树u的大小,包括u本身
    dep[u] 结点u的深度,根节点深度为1
    重儿子 节点u的子树中,sz最大的那个子树的根,用son[u]表示(叶结点无重儿子)
    重链 由连接结点与其重儿子的边连接而成的路径(单独的结点也是一条重链)
    轻链 又叫轻边,树中除了重链剩下的边
    top[u] 结点u所在重链dep最小的结点(也就是最上面的点)
    id[u] 树上结点u在线段树中的编号
    

    原谅我语文不好。。。
    放张图吧QAQ

    画的真帅
    如图绿色框起来的是重链,橙色的是top,红的边是重链里的边,蓝的是轻边,可以发现轻边连接两条重链
    举个栗子:

    top[8]=8;top[7]=1;top[5]=2;son[1]=2;son[2]=8;son[6]=7;sz[2]=4;
    

    预处理code:

    inline ll dfs1(ll from){
        ll tmp=0,tmx=0,ti=0;
        for(ll i=head[from];i;i=s[i].nxt){
            ll to=s[i].to;
            if(to!=f[from]){
                dep[to]=dep[from]+1;//处理dep
                f[to]=from;//处理fa
                tmp=dfs1(to);
                sz[from]+=tmp;//处理sz
                if(tmp>tmx)//处理重儿子
                    tmx=tmp,ti=to;
            }
        }
        sz[from]++;
        son[from]=ti;
        return sz[from];
    }
    inline void dfs2(ll from,ll tp){
        id[from]=++num,vt[num]=v[from];//处理id和线段树上初始点权
        if(son[from])
            top[son[from]]=tp,dfs2(son[from],tp);//先递归处理重儿子,使得重链编号连在一起
        for(ll i=head[from];i;i=s[i].nxt){
            ll to=s[i].to;
            if(to!=f[from]&&to!=son[from])
                top[to]=to,dfs2(to,to);//递归处理轻儿子
        }
    }
    

    预处理好了,那么如何在树链上进行更改操作呢?
    如图,先看一种特殊情况,将x到y的路径全部加k(y为x祖先结点)

    如上图,x->top[x]的路径是一条重链,top[x]->fa[top[x]]是一条轻边,fa[top[x]]->y的路径是一条重链。
    将x到y区间加,很显然就是先将x到top[x]在线段树上区间加k,然后在将fa[top[x]]到y在线段树上加k。
    代码如下(实际上这一段并没有什么luan用)
    Code:

    void cRange(int x,int y,int k){//区间修改
        while(top[x]!top[y]){//不在同一条重链上
            S.change(1,1,n,id[top[x]],id[x],k);//线段树上区间加k
            x=fa[top[x]];//将x变为fa[top[x]]
        }
        S.change(1,1,n,id[y],id[x],k);//将x到y区间加
    }
    

    询问也类似于区间加,只是多记录个res

    int qRange(int x,int y,int k){//区间查询
        int res=0;
        while(top[x]!top[y]){//不在同一条重链上
            res+=S.ask(1,1,n,id[top[x]],id[x],k);//查询x到top[x]
            res%=md;
            x=fa[top[x]];//将x变为fa[top[x]]
        }
        res+=S.change(1,1,n,id[y],id[x],k);//查询x到y
        return res%md;
    }
    

    讨论第二种情况:x与y不在同一条链上(注:这里不是指重链)

    考虑类似倍增的方法,将x与y不断往上跳,直到在同一条重链上为止
    1、比较x、y top的大小,将x的top置为dep较大的那个
    2、在线段树上处理x至其top结点
    3、将x置为fa[top[x]]
    4、如果x、y不在同一条重链上,返回1
    5、处理区间x至y
    详见代码:
    Code:

    inline void cRange(int x,int y,int z){
    	while(top[x]!=top[y]){
    		if(dep[top[x]]<dep[top[y]])
    			swap(x,y);
    		T.change(1,1,n,id[top[x]],id[x],z);
    		x=f[top[x]];
    	}
    	if(dep[x]<dep[y])
    		swap(x,y);
    	T.change(1,1,n,id[y],id[x],z);
    }
    inline int qRange(int x,int y){
    	int res=0;
    	while(top[x]!=top[y]){
    		if(dep[top[x]]<dep[top[y]])
    			swap(x,y);
    		res+=T.ask(1,1,n,id[top[x]],id[x]);
    		res%=md;
    		x=f[top[x]];
    	}
    	if(dep[x]<dep[y])
    		swap(x,y);
    	res+=T.ask(1,1,n,id[y],id[x]);
    	return res%md;
    }
    

    然而还有两种操作,对树的修改与查询,如何解决?
    显然,可以发现,对于树上任意一棵子树来说,它们在线段树上的编号(即id)是连在一起的(废话)
    于是,很显然,对于树的操作可以这么写:

    inline void cTree(int x,int z){
    	T.change(1,1,n,id[x],id[x]+sz[x]-1,z);
    }
    inline int qTree(int x){
    	return T.ask(1,1,n,id[x],id[x]+sz[x]-1)%md;
    }
    

    至此,树链剖分的模板就搞定了,下面是代码:

    #include<bits/stdc++.h>
    #define ll long long
    #define INF 2147483647
    #define mem(i,j) memset(i,j,sizeof(i))
    #define F(i,j,n) for(register int i=j;i<=n;i++)
    #define md p
    using namespace std;
    struct hahaha{
    	int from,to,nxt;
    }s[200010];
    int n,m,r,p,head[200010],cnt=0,pls[1000010];
    int v[100010],f[100010],son[100010];
    int dep[100010],top[100010],sz[100010];
    int id[1000010],vt[1000010],num=0;
    inline int read(){
    	int datta=0;char chchc=getchar();bool okoko=0;
    	while(chchc<'0'||chchc>'9'){if(chchc=='-')okoko=1;chchc=getchar();}
    	while(chchc>='0'&&chchc<='9'){datta=datta*10+chchc-'0';chchc=getchar();}
    	if(okoko==1)return -datta;
    	return datta;
    }
    inline void ins(int from,int to){
    	s[++cnt].from=from;
    	s[cnt].to=to;
    	s[cnt].nxt=head[from];
    	head[from]=cnt;
    }
    inline int dfs1(int from){
    	int tmp=0,tmx=0,ti=0;
    	for(int i=head[from];i;i=s[i].nxt){
    		int to=s[i].to;
    		if(to!=f[from]){
    			dep[to]=dep[from]+1;
    			f[to]=from;
    			tmp=dfs1(to);
    			sz[from]+=tmp;
    			if(tmp>tmx)
    				tmx=tmp,ti=to;
    		}
    	}
    	sz[from]++;
    	son[from]=ti;
    	return sz[from];
    }
    inline void dfs2(int from,int tp){
    	id[from]=++num,vt[num]=v[from];
    	if(son[from])
    		top[son[from]]=tp,dfs2(son[from],tp);
    	for(int i=head[from];i;i=s[i].nxt){
    		int to=s[i].to;
    		if(to!=f[from]&&to!=son[from])
    			top[to]=to,dfs2(to,to);
    	}
    }
    struct Segment_Tree{
    	#define ls u<<1
    	#define rs u<<1|1
    	#define mid ((l+r)>>1)
    	int tree[1000010];
    	void updata(int u){
    		tree[u]=(tree[ls]+tree[rs])%md;
    	}
    	void pushdown(int u,int l,int r){
    		if(!pls[u])
    			return ;
    		pls[ls]+=pls[u];
    		pls[rs]+=pls[u];
    		tree[ls]+=(mid-l+1)*(pls[u]);
    		tree[rs]+=(r-mid)*(pls[u]);
    		pls[u]=0;
    	}
    	void build_tree(int u,int l,int r){
    		if(l==r){
    			tree[u]=vt[l];
    			return ;
    		}
    		build_tree(ls,l,mid);
    		build_tree(rs,mid+1,r);
    		updata(u);
    	}
    	void change(int u,int l,int r,int x,int y,int z){
    		if(x<=l&&r<=y){
    			pls[u]+=z;
    			tree[u]+=(r-l+1)*z;
    			return ;
    		}
    		pushdown(u,l,r);
    		if(x<=mid)
    			change(ls,l,mid,x,y,z);
    		if(y>=mid+1)
    			change(rs,mid+1,r,x,y,z);
    		updata(u);
    	}
    	int ask(int u,int l,int r,int x,int y){
    		int res=0;
    		if(x<=l&&r<=y)
    			return tree[u];
    		pushdown(u,l,r);;
    		if(x<=mid)
    			res+=ask(ls,l,mid,x,y);
    		if(y>=mid+1)
    			res+=ask(rs,mid+1,r,x,y);
    		return res;
    	}
    }T;
    inline void cRange(int x,int y,int z){
    	while(top[x]!=top[y]){
    		if(dep[top[x]]<dep[top[y]])
    			swap(x,y);
    		T.change(1,1,n,id[top[x]],id[x],z);
    		x=f[top[x]];
    	}
    	if(dep[x]<dep[y])
    		swap(x,y);
    	T.change(1,1,n,id[y],id[x],z);
    }
    inline int qRange(int x,int y){
    	int res=0;
    	while(top[x]!=top[y]){
    		if(dep[top[x]]<dep[top[y]])
    			swap(x,y);
    		res+=T.ask(1,1,n,id[top[x]],id[x]);
    		res%=md;
    		x=f[top[x]];
    	}
    	if(dep[x]<dep[y])
    		swap(x,y);
    	res+=T.ask(1,1,n,id[y],id[x]);
    	return res%md;
    }
    inline void cTree(int x,int z){
    	T.change(1,1,n,id[x],id[x]+sz[x]-1,z);
    }
    inline int qTree(int x){
    	return T.ask(1,1,n,id[x],id[x]+sz[x]-1)%md;
    }
    int main(){
    	n=read();m=read();r=read();p=read();
    	F(i,1,n)
    		v[i]=read();
    	F(i,1,n-1){
    		int from=read(),to=read();
    		ins(from,to);ins(to,from);
    	}
    	dep[r]=1;
    	dfs1(r);
    	top[r]=r;
    	dfs2(r,r);
    	T.build_tree(1,1,n);
    	F(p,1,m){
    		int kd=read(),x,y,z;
    		if(kd==1)
    			x=read(),y=read(),z=read(),cRange(x,y,z);
    		if(kd==2)
    			x=read(),y=read(),printf("%d
    ",qRange(x,y));
    		if(kd==3)
    			x=read(),z=read(),cTree(x,z);
    		if(kd==4)
    		    printf("%d
    ",qTree(read()));
    	}
    	return 0;
    }
    

    Ⅳ、例题

    [Zjoi2008]树的统计

    题目传送门
    题意很简单,树上单点修改,区间查询最大值与和,树剖裸题,线段树练手题

    #include<bits/stdc++.h>
    #define ll long long
    #define INF 2147483647
    #define mem(i,j) memset(i,j,sizeof(i))
    #define F(i,j,n) for(register ll i=j;i<=n;i++)
    #define md p
    using namespace std;
    struct hahaha{
        ll from,to,nxt;
    }s[200010];
    ll n,m,r,p,head[200010],cnt=0,pls[1000010];
    ll v[100010],f[100010],son[100010];
    ll dep[100010],top[100010],sz[100010];
    ll id[1000010],vt[1000010],num=0;
    inline ll read(){
        ll datta=0;char chchc=getchar();bool okoko=0;
        while(chchc<'0'||chchc>'9'){if(chchc=='-')okoko=1;chchc=getchar();}
        while(chchc>='0'&&chchc<='9'){datta=datta*10+chchc-'0';chchc=getchar();}
        if(okoko==1)return -datta;
        return datta;
    }
    inline void ins(ll from,ll to){
        s[++cnt].from=from;
        s[cnt].to=to;
        s[cnt].nxt=head[from];
        head[from]=cnt;
    }
    inline ll dfs1(ll from){
        ll tmp=0,tmx=0,ti=0;
        for(ll i=head[from];i;i=s[i].nxt){
            ll to=s[i].to;
            if(to!=f[from]){
                dep[to]=dep[from]+1;
                f[to]=from;
                tmp=dfs1(to);
                sz[from]+=tmp;
                if(tmp>tmx)
                    tmx=tmp,ti=to;
            }
        }
        sz[from]++;
        son[from]=ti;
        return sz[from];
    }
    inline void dfs2(ll from,ll tp){
        id[from]=++num,vt[num]=v[from];
        if(son[from])
            top[son[from]]=tp,dfs2(son[from],tp);
        for(ll i=head[from];i;i=s[i].nxt){
            ll to=s[i].to;
            if(to!=f[from]&&to!=son[from])
                top[to]=to,dfs2(to,to);
        }
    }
    struct Segment_Tree{
        #define ls u<<1
        #define rs u<<1|1
        #define mid ((l+r)>>1)
        ll tree[1000010],mx[1000010];
        void updata(ll u){
            tree[u]=tree[ls]+tree[rs];
            mx[u]=max(mx[ls],mx[rs]);
        }
        void build_tree(ll u,ll l,ll r){
            mx[u]=-2147483647;
            if(l==r){
                tree[u]=mx[u]=vt[l];
                return ;
            }
            build_tree(ls,l,mid);
            build_tree(rs,mid+1,r);
            updata(u);
        }
        void change(ll u,ll l,ll r,ll x,ll z){
            if(l==r){
                tree[u]=z;
                mx[u]=z;
                return ;
            }
            if(x<=mid)
                change(ls,l,mid,x,z);
            else
                change(rs,mid+1,r,x,z);
            updata(u);
        }
        ll askmx(ll u,ll l,ll r,ll x,ll y){
            ll res=-214748364700;
            if(x<=l&&r<=y)
                return mx[u];
            if(x<=mid)
                res=askmx(ls,l,mid,x,y);
            if(y>=mid+1)
                res=max(res,askmx(rs,mid+1,r,x,y));
            return res;
        }
        ll ask(ll u,ll l,ll r,ll x,ll y){
            ll res=0;
            if(x<=l&&r<=y)
                return tree[u];
            if(x<=mid)
                res+=ask(ls,l,mid,x,y);
            if(y>=mid+1)
                res+=ask(rs,mid+1,r,x,y);
            return res;
        }
    }T;
    inline ll qRangemx(ll x,ll y){
        ll res=-214748364700;
        while(top[x]!=top[y]){
            if(dep[top[x]]<dep[top[y]])
                swap(x,y);
            res=max(res,T.askmx(1,1,n,id[top[x]],id[x]));
            x=f[top[x]];
        }
        if(dep[x]<dep[y])
            swap(x,y);
        res=max(res,T.askmx(1,1,n,id[y],id[x]));
        return res;
    }
    inline ll qRange(ll x,ll y){
        ll res=0;
        while(top[x]!=top[y]){
            if(dep[top[x]]<dep[top[y]])
                swap(x,y);
            res+=T.ask(1,1,n,id[top[x]],id[x]);
            x=f[top[x]];
        }
        if(dep[x]<dep[y])
            swap(x,y);
        res+=T.ask(1,1,n,id[y],id[x]);
        return res;
    }
    int main(){
        n=read();r=1;
        F(i,1,n-1){
            ll from=read(),to=read();
            ins(from,to);ins(to,from);
        }
        F(i,1,n)
            v[i]=read();
        dep[r]=1;
        dfs1(r);
        top[r]=r;
        dfs2(r,r);
        T.build_tree(1,1,n);
        m=read();
        F(p,1,m){
            char ch=getchar();
            while(ch!='Q'&&ch!='C')
                ch=getchar();
            if(ch=='Q')
                ch=getchar();
            ll x=read(),y=read();
            if(ch=='C')
                T.change(1,1,n,id[x],y);
            if(ch=='S')
                printf("%lld
    ",qRange(x,y));
            if(ch=='M')
                printf("%lld
    ",qRangemx(x,y));
        }
        return 0;
    }
    

    Spoj 2798 Qtree3

    传送门
    单点修改,查询rt到某个结点第一个黑色点
    考虑在线段树上维护数组lft,表示该区间内最靠近rt并且为黑色点的编号,转移时直接取左右儿子的max即可

    #include<bits/stdc++.h>
    #define ll long long
    #define INF 2147483647
    #define mem(i,j) memset(i,j,sizeof(i))
    #define F(i,j,n) for(register int i=j;i<=n;i++)
    using namespace std;
    struct hahaha{
        int from,to,nxt;
    }s[200010];
    int n,m,r,p,head[200010],cnt=0,iid[100010];
    int v[100010],f[100010],son[100010];
    int dep[100010],top[100010],sz[100010],z[100010],nm[100010];
    int id[100010],vt[100010],num=0;
    inline int read(){
        int datta=0;char chchc=getchar();bool okoko=0;
        while(chchc<'0'||chchc>'9'){if(chchc=='-')okoko=1;chchc=getchar();}
        while(chchc>='0'&&chchc<='9'){datta=datta*10+chchc-'0';chchc=getchar();}
        if(okoko==1)return -datta;
        return datta;
    }
    inline void ins(int from,int to){
        s[++cnt].from=from;
        s[cnt].to=to;
        s[cnt].nxt=head[from];
        head[from]=cnt;
    }
    inline int dfs1(int from){
        int tmp=0,tmx=0,ti=0;
        for(int i=head[from];i;i=s[i].nxt){
            int to=s[i].to;
            if(to!=f[from]){
                dep[to]=dep[from]+1;
                f[to]=from;
                v[to]=z[i];
                nm[(i+1)/2]=to;
                tmp=dfs1(to);
                sz[from]+=tmp;
                if(tmp>tmx)
                    tmx=tmp,ti=to;
            }
        }
        sz[from]++;
        son[from]=ti;
        return sz[from];
    }
    inline void dfs2(int from,int tp){
        id[from]=++num,iid[num]=from,vt[num]=v[from];
        if(son[from])
            top[son[from]]=tp,dfs2(son[from],tp);
        for(int i=head[from];i;i=s[i].nxt){
            int to=s[i].to;
            if(to!=f[from]&&to!=son[from])
                top[to]=to,dfs2(to,to);
        }
    }
    struct Segment_Tree{
        #define ls u<<1
        #define rs u<<1|1
        #define mid ((l+r)>>1)
        int tree[1000010],lft[1000010];
        void updata(int u){
            tree[u]=max(tree[ls],tree[rs]);
            lft[u]=min(lft[ls],lft[rs]);
        }
        void build_tree(int u,int l,int r){
            lft[u]=2147483647;
            if(l==r)
                return ;
            build_tree(ls,l,mid);
            build_tree(rs,mid+1,r);
        }
        void change(int u,int l,int r,int x){
            if(l==r){
                tree[u]^=1;
                lft[u]=tree[u]?l:2147483647;
                return ;
            }
            if(x<=mid)
                change(ls,l,mid,x);
            else
                change(rs,mid+1,r,x);
            updata(u);
        }
        int ask(int u,int l,int r,int x,int y){
            int res=2147483647;
            if(x<=l&&r<=y)
                return lft[u];
            if(x<=mid)
                res=ask(ls,l,mid,x,y);
            if(y>=mid+1)
                res=min(res,ask(rs,mid+1,r,x,y));
            return res;
        }
    }T;
    inline int qRange(int x,int y){
        int res=2147483647;
        while(top[x]!=top[y]){
            if(dep[top[x]]<dep[top[y]])
                swap(x,y);
            res=min(res,T.ask(1,1,n,id[top[x]],id[x]));
            x=f[top[x]];
        }
        if(dep[x]<dep[y])
            swap(x,y);
        res=min(res,T.ask(1,1,n,id[y],id[x]));
        return res;
    }
    int main(){
        n=read();m=read();r=1;
        F(i,1,n-1){
            int from=read(),to=read();
            ins(from,to);ins(to,from);
        }
        dep[r]=1;
        dfs1(r);
        top[r]=r;
        dfs2(r,r);
        T.build_tree(1,1,n);
        while(m--){
            int kd=read();
            int x=read();
            if(kd==0)
                T.change(1,1,n,id[x]);
            if(kd==1){
                int ans=qRange(r,x);
                printf("%d
    ",ans==2147483647?-1:iid[ans]);
            }
        } 
        return 0;
    }
    

    [Usaco2011 Dec]Grass Planting

    传送门
    之前的树剖都是在结点上进行操作,这道题要求在边权上进行修改与查询
    如何转化?
    考虑将一条边dep较大的那个端点作为一条边的权值,即将边权转移到靠下端的结点上
    具体细节看代码注释

    #include<bits/stdc++.h>
    #define ll long long
    #define INF 2147483647
    #define mem(i,j) memset(i,j,sizeof(i))
    #define F(i,j,n) for(register int i=j;i<=n;i++)
    using namespace std;
    struct hahaha{
    	int from,to,nxt;
    }s[200010];
    int n,m,r,p,head[200010],cnt=0;
    int v[100010],f[100010],son[100010];
    int dep[100010],top[100010],sz[100010],z[100010],nm[100010];
    int id[1000010],vt[1000010],num=0;
    inline int read(){
    	int datta=0;char chchc=getchar();bool okoko=0;
    	while(chchc<'0'||chchc>'9'){if(chchc=='-')okoko=1;chchc=getchar();}
    	while(chchc>='0'&&chchc<='9'){datta=datta*10+chchc-'0';chchc=getchar();}
    	if(okoko==1)return -datta;
    	return datta;
    }
    inline void ins(int from,int to){
    	s[++cnt].from=from;
    	s[cnt].to=to;
    	s[cnt].nxt=head[from];
    	head[from]=cnt;
    }
    inline int dfs1(int from){
    	int tmp=0,tmx=0,ti=0;
    	for(int i=head[from];i;i=s[i].nxt){
    		int to=s[i].to;
    		if(to!=f[from]){
    			dep[to]=dep[from]+1;
    			f[to]=from;
    			v[to]=z[i];
    			nm[(i+1)/2]=to;
    			tmp=dfs1(to);
    			sz[from]+=tmp;
    			if(tmp>tmx)
    				tmx=tmp,ti=to;
    		}
    	}
    	sz[from]++;
    	son[from]=ti;
    	return sz[from];
    }
    inline void dfs2(int from,int tp){
    	id[from]=++num,vt[num]=v[from];
    	if(son[from])
    		top[son[from]]=tp,dfs2(son[from],tp);
    	for(int i=head[from];i;i=s[i].nxt){
    		int to=s[i].to;
    		if(to!=f[from]&&to!=son[from])
    			top[to]=to,dfs2(to,to);
    	}
    }
    struct Segment_Tree{
    	#define ls u<<1
    	#define rs u<<1|1
    	#define mid ((l+r)>>1)
    	int tree[1000010];
    	void updata(int u){
    		tree[u]=max(tree[ls],tree[rs]);
    	}
    	void build_tree(int u,int l,int r){
    		if(l==r){
    			tree[u]=vt[l];
    			return ;
    		}
    		build_tree(ls,l,mid);
    		build_tree(rs,mid+1,r);
    		updata(u);
    	}
    	void change(int u,int l,int r,int x,int z){
    		if(l==r){
    			tree[u]=z;
    			return ;
    		}
    		if(x<=mid)
    			change(ls,l,mid,x,z);
    		else
    			change(rs,mid+1,r,x,z);
    		updata(u);
    	}
    	int ask(int u,int l,int r,int x,int y){
    		int res=0;
    		if(x<=l&&r<=y)
    			return tree[u];
    		if(x<=mid)
    			res=ask(ls,l,mid,x,y);
    		if(y>=mid+1)
    			res=max(res,ask(rs,mid+1,r,x,y));
    		return res;
    	}
    }T;
    inline int qRange(int x,int y){
    	int res=0;
    	while(top[x]!=top[y]){
    		if(dep[top[x]]<dep[top[y]])
    			swap(x,y);
    		res=max(res,T.ask(1,1,n,id[top[x]],id[x]));
    		x=f[top[x]];
    	}
    	if(x==y)//防止多累加答案
    		return res;
    	if(dep[x]<dep[y])
    		swap(x,y);
    	res=max(res,T.ask(1,1,n,id[y]+1,id[x]));//id[y]+1即为id[son[y]],这么写是因为id[y]+1存着y->son[y]这条边
    	return res;
    }
    int main(){
    	m=read();
    	while(m--){
    		mem(s,0);mem(head,0);cnt=0;mem(v,0);mem(f,0);mem(son,0);mem(dep,0);mem(top,0);mem(sz,0);
    		mem(z,0);mem(nm,0);mem(id,0);mem(vt,0);num=0;mem(T.tree,0);
    		n=read();r=1;
    		F(i,1,n-1){
    			int from=read(),to=read();
    			z[i*2-1]=z[i*2]=read();
    			ins(from,to);ins(to,from);
    		}
    		dep[r]=1;
    		dfs1(r);
    		top[r]=r;
    		dfs2(r,r);
    		T.build_tree(1,1,n);
    		while(1){
    			char ch=getchar();
    			while(ch!='Q'&&ch!='C'&&ch!='D')
    				ch=getchar();
    			if(ch=='D')
    				return 0;
    			int x=read(),y=read();
    			if(ch=='C')
    				T.change(1,1,n,nm[x],y);
    			if(ch=='Q')
    				printf("%d
    ",qRange(x,y));
    		}
    	}
    	return 0;
    }
    
  • 相关阅读:
    springmvc 注解式开发 处理器方法的返回值
    springmvc 注解式开发 接收请求参数
    【洛谷P1379】八数码难题 状压bfs
    【模板】ST表
    【POJ1741】Tree
    【洛谷P1073】最优贸易
    【POJ3662】Telephone Lines dij + 二分答案
    【模板】spfa
    【洛谷P2384】最短乘积路径
    【bzoj2038】小Z的袜子
  • 原文地址:https://www.cnblogs.com/hzf29721/p/10201002.html
Copyright © 2020-2023  润新知