树链剖分

树链剖分——轻重链剖分

blog

https://blog.csdn.net/qq_40482358/article/details/89676297

树上的问题转化成序列问题(然后就快乐线段树维护)

概括一下就是把树划分为若干条链,然后一个链的dfs序是连续的,对于树上两点,我们可以通过跳链直到两者在同一条链上,然后链上的权值就是区间问题

定义:

重儿子:所有儿子中子树(siz)最大的点,其他的都叫轻儿子

重链:所有重儿子穿成的(许多条)链,其他的都叫轻链

1

性质:

1.若(u,v)为一条轻边,$ size(v)<size(u)/2$

2.从根节点到任意节点的路径 经过的轻边数量 小于 logn,经过的重边数量 也小于 logn

可以得出,树链剖分的时间复杂度为(O(nlog^2n)) ,是一种优秀的算法。

求出重儿子,深度,子树大小

void dfs_son(int x,int f){
	siz[x]=1;fa[x]=f;dep[x]=dep[f]+1;
	for(int i=hd[x];i;i=nxt[i]){
		int y=to[i];
		if(y==f)continue;
		dfs_son(y,x);
		siz[x]+=siz[y];
		if(siz[y]>siz[son[x]])son[x]=y;
	}
}

接下来把轻重儿子连成链,所以我们就一定要让链上的点连续

我们从根开始一遍DFS,如果有重儿子就先走重儿子(将一条重链放到一个区间内),搜到叶子节点后回溯回去再去搜轻儿子(轻链)

板子

要处理的问题:

  • 1将树从x到y结点最短路径上所有节点的值都加上z
  • 2求树从x到y结点最短路径上所有节点的值之和
  • 3将以x为根节点的子树内所有节点值都加上z
  • 4求以x为根节点的子树内所有节点值之和

由于dfs序中一颗子树是连续的一段,3,4就比较好处理了

对于树上任意两点的路径,设所在链顶端的深度更深的那个点为x点

  • ans加上x点到x所在链顶端 这一段区间的点权和
  • 把x跳到x所在链顶端的那个点的 fa

不停执行这两个步骤,直到两个点处于一条链上,这时再加上此时两个点的区间和即可

(O(log^2n))查询

#include <cstdio>
#include <iostream>
#include <vector>
using namespace std;
#define ls (p<<1)
#define rs (p<<1|1)
const int N=5e5;
typedef long long LL;
inline int read(){
    int x=0;char ch=getchar();
    while(!isdigit(ch)) ch=getchar();
    while(isdigit(ch)) {x=x*10+ch-'0';ch=getchar();}
    return x;
}
int n,m,root,mod,val[N];
int to[N],nxt[N],hd[N],tot;
inline void add(int x,int y){
	to[++tot]=y;nxt[tot]=hd[x];hd[x]=tot;
}

int siz[N],dep[N],fa[N],son[N];
void dfs_son(int x,int f){	
    siz[x]=1;fa[x]=f;dep[x]=dep[f]+1;
    for(int i=hd[x];i;i=nxt[i]){
        int y=to[i];
        if(y==f) continue;
        dfs_son(y,x);
        siz[x]+=siz[y];
        if(siz[y]>siz[son[x]]) son[x]=y;
    }
}
int dfn[N],rev[N],top[N],dfn_cnt;
void dfs_chain(int x,int tp){
    dfn[x]=++dfn_cnt;rev[dfn_cnt]=x;top[x]=tp;
    if(son[x]) dfs_chain(son[x],tp);
    for(int i=hd[x];i;i=nxt[i]){
        int y=to[i];
        if(dfn[y]) continue;
        dfs_chain(y,y);
    }
}

struct tree{
    int l,r;
    LL tag,sum;
}t[N];
inline void push_up(int p){
    t[p].sum=(t[ls].sum+t[rs].sum)%mod;
}
void build(int l,int r,int p){
    t[p].l=l;t[p].r=r;
    if(l==r){ t[p].sum=val[rev[l]];return; }
    int mid=(l+r)>>1;
    build(l,mid,ls);
    build(mid+1,r,rs);
    push_up(p);
}
void push_down(int p){
    if(!t[p].tag) return;
    (t[ls].tag+=t[p].tag)%=mod;
    (t[rs].tag+=t[p].tag)%=mod;
    (t[ls].sum+=(t[ls].r-t[ls].l+1)*t[p].tag)%=mod;
    (t[rs].sum+=(t[rs].r-t[rs].l+1)*t[p].tag)%=mod;
    t[p].tag=0;
}
void modify(int L,int R,LL v,int p){
    if(L<=t[p].l&&t[p].r<=R){
        (t[p].tag+=v)%=mod;
        (t[p].sum+=(t[p].r-t[p].l+1)*v)%=mod;
        return;
    }
    push_down(p);
    int mid=(t[p].l+t[p].r)>>1;
    if(L<=mid) modify(L,R,v,ls);
    if(R>mid) modify(L,R,v,rs);
    push_up(p);
}
LL query(int L,int R,int p){
    if(L<=t[p].l&&t[p].r<=R) return t[p].sum;
    
    push_down(p);
    int mid=(t[p].l+t[p].r)>>1;
    LL res=0;
    if(L<=mid) (res+=query(L,R,ls))%=mod;
    if(R>mid) (res+=query(L,R,rs))%=mod;
    return res;
}
void update(int x,int y,LL v){
    while(top[x]!=top[y]){
        if(dep[top[x]]<dep[top[y]]) swap(x,y);
        modify(dfn[top[x]],dfn[x],v,1);
        x=fa[top[x]];
    }
    if(dfn[x]>dfn[y]) swap(x,y);
    modify(dfn[x],dfn[y],v,1);
}
LL sum(int x,int y){
    LL res=0;
    while(top[x]!=top[y]){
        if(dep[top[x]]<dep[top[y]]) swap(x,y);
        (res+=query(dfn[top[x]],dfn[x],1))%=mod;
        x=fa[top[x]];
    }
    if(dfn[x]>dfn[y]) swap(x,y);
    (res+=query(dfn[x],dfn[y],1))%=mod;   
    return res;
}
int main(){
    n=read();m=read();root=read();mod=read();
    for(int i=1;i<=n;i++) val[i]=read();
    for(int i=1,x,y;i<n;i++){
        x=read();y=read();
        add(x,y);add(y,x);
    }
    dfs_son(root,0);
    dfs_chain(root,root);
    build(1,n,1);
    for(int i=1,x,y,z,op;i<=m;i++){
        op=read();x=read();
        if(op==1){
            y=read();z=read();
            update(x,y,z);
        }
        if(op==2){
            y=read();
            printf("%lld
",sum(x,y));
        }
        if(op==3){
            z=read();
            modify(dfn[x],dfn[x]+siz[x]-1,z,1);
        }
        if(op==4){
            printf("%lld
",query(dfn[x],dfn[x]+siz[x]-1,1));
        }
    }
    return 0;
}

类板子:

https://www.luogu.com.cn/problem/P3178

https://www.luogu.com.cn/problem/P2590

https://www.luogu.com.cn/problem/P3128

边权转化:

Qtree1 具体的把边权赋给深度较深的点(在dfs_son时),但有个问题是,我们查 (x,y) 上的最大值,其lca维护的是 lca-->fa[lca] 的边权,实际是不可查到的,所以我们只需在查询最后一步

https://www.luogu.com.cn/problem/P303

类似上面

软件包管理器

安装—— 1到x的路径赋值为1

卸载—— x的子树赋值为0

ans=操作前的总和和操作后的总和

#include<bits/stdc++.h>
using namespace std;
#define N 100010
#define ls (p<<1)
#define rs (p<<1|1)
inline int read() {
	int x=0,f=1;char ch=getchar();
	while(!isdigit(ch)) {if(ch=='-')f=-1;ch=getchar();}
	while(isdigit(ch)) {x=x*10+ch-'0';ch=getchar();}
	return f*x;
}
int n,q;
char op[10];
int to[N<<1],nxt[N<<1],hd[N],tot;
inline void add(int x,int y) {
	to[++tot]=y;nxt[tot]=hd[x];hd[x]=tot;
}
int siz[N],fa[N],son[N],dep[N];
void dfs_son(int x,int f) {
	siz[x]=1;fa[x]=f;dep[x]=dep[f]+1;
	for(int i=hd[x];i;i=nxt[i]) {
		int y=to[i];
		if(y==f)continue;
		dfs_son(y,x);
		siz[x]+=siz[y];
		if(siz[y]>siz[son[x]])son[x]=y;
	}
}
int dfn[N],top[N],dfn_cnt;
void dfs_chain(int x,int tp) {
	top[x]=tp;
	dfn[x]=++dfn_cnt;
	if(son[x])dfs_chain(son[x],tp);
	for(int i=hd[x];i;i=nxt[i]) {
		int y=to[i];
		if(dfn[y])continue;
		dfs_chain(y,y);
	}
}
struct TREE{
	int l,r,sum,tag;
}t[N<<2];

void pushdown(int p) {
	if(t[p].tag==-1)return;
	t[ls].tag=t[p].tag;
	t[rs].tag=t[p].tag;
	t[ls].sum=t[p].tag*(t[ls].r-t[ls].l+1);
	t[rs].sum=t[p].tag*(t[rs].r-t[rs].l+1);
	t[p].tag=-1;
}
void build(int L,int R,int p) {
	t[p].l=L;t[p].r=R;t[p].tag=-1;
	if(L==R) return;
	int mid=(L+R)>>1;
	build(L,mid,ls);
	build(mid+1,R,rs);
	t[p].sum=t[ls].sum+t[rs].sum;
}
void modify(int L,int R,int add,int p) {
	if(L<=t[p].l&&t[p].r<=R) {
		t[p].sum=add*(t[p].r-t[p].l+1);
		t[p].tag=add;return;
	}
	pushdown(p);
	int mid=(t[p].l+t[p].r)>>1;
	if(L<=mid) modify(L,R,add,ls);
	if(mid<R) modify(L,R,add,rs);
	t[p].sum=t[ls].sum+t[rs].sum;
}
void update(int x,int y,int add) {
	while(top[x]!=top[y]) {
		if(dep[top[x]]<dep[top[y]]) swap(x,y);
		modify(dfn[top[x]],dfn[x],add,1);
		x=fa[top[x]];
	}
	if(dfn[x]>dfn[y]) swap(x,y);
	modify(dfn[x],dfn[y],add,1);
}
int main() {
	n=read();
	for(int i=2,x;i<=n;++i) {
		x=read()+1;
		add(x,i),add(i,x);
	}
	dfs_son(1,0);
	dfs_chain(1,1);
	build(1,n,1); 

	q=read();
	while(q--) {
		int a,bef;
		bef=t[1].sum;
		scanf("%s",op);
		a=read()+1;
		if(op[0]=='i') {
			update(1,a,1);
			printf("%d
",abs(bef-t[1].sum));
		} else {
			modify(dfn[a],dfn[a]+siz[a]-1,0,1);
			printf("%d
",abs(bef-t[1].sum));
		}
	}
	return 0;
	
}

染色

好题!!

细节好多哦

就是要维护个lcol,rcol

每次合并(包括询问合并)的时候

if(t[ls].rc==t[rs].lc) ans--
#include<bits/stdc++.h>
using namespace std;
#define N 100010
#define ls (p<<1)
#define rs (p<<1|1)
inline int read() {
	int x=0,f=1;char ch=getchar();
	while(!isdigit(ch)) {if(ch=='-')f=-1;ch=getchar();}
	while(isdigit(ch)) {x=x*10+ch-'0';ch=getchar();}
	return f*x;
}
int n,q,a[N];

int to[N<<1],nxt[N<<1],hd[N],tot;
inline void add(int x,int y) {
	to[++tot]=y;nxt[tot]=hd[x];hd[x]=tot;
}
int siz[N],fa[N],son[N],dep[N];
void dfs_son(int x,int f) {
	siz[x]=1;fa[x]=f;dep[x]=dep[f]+1;
	for(int i=hd[x];i;i=nxt[i]) {
		int y=to[i];
		if(y==f)continue;
		dfs_son(y,x);
		siz[x]+=siz[y];
		if(siz[y]>siz[son[x]])son[x]=y;
	}
}
int dfn[N],rev[N],top[N],dfn_cnt;
void dfs_chain(int x,int tp) {
	top[x]=tp;
	dfn[x]=++dfn_cnt;rev[dfn_cnt]=x;
	if(son[x])dfs_chain(son[x],tp);
	for(int i=hd[x];i;i=nxt[i]) {
		int y=to[i];
		if(dfn[y])continue;
		dfs_chain(y,y);
	}
}
struct TREE{
	int val,lc,rc,l,r,tag;
}t[N<<2];
inline void pushup(int p) {
	t[p].val=t[ls].val+t[rs].val-(t[ls].rc==t[rs].lc);
	t[p].lc=t[ls].lc;t[p].rc=t[rs].rc;//Q
}
void pushdown(int p) {//Q
	if(t[p].tag==-1)return;
	t[ls].tag=t[rs].tag=t[p].tag;
	t[ls].val=t[rs].val=1;
	t[ls].lc=t[ls].rc=t[p].tag;
	t[rs].lc=t[rs].rc=t[p].tag;
	t[p].tag=-1;
}
void build(int L,int R,int p) {
	t[p].l=L;t[p].r=R;t[p].tag=-1;
	if(L==R) {//Q
		t[p].val=1;
		t[p].lc=t[p].rc=a[rev[L]];
		return;
	}
	int mid=(L+R)>>1;
	build(L,mid,ls);
	build(mid+1,R,rs);
	pushup(p);
}
void modify(int L,int R,int add,int p) {
	if(L<=t[p].l&&t[p].r<=R) {//Q
		t[p].val=1;
		t[p].lc=t[p].rc=t[p].tag=add;
		return;
	}
	pushdown(p);
	int mid=(t[p].l+t[p].r)>>1;
	if(L<=mid) modify(L,R,add,ls);
	if(mid<R) modify(L,R,add,rs);
	pushup(p);
}
TREE query(int L,int R,int p) {
	if(L<=t[p].l&&t[p].r<=R) return t[p];
	pushdown(p);
	int mid=(t[p].l+t[p].r)>>1;
	if(R<=mid) return query(L,R,ls);
	else if(L>mid) return query(L,R,rs);
	else {//Q
		TREE s1=query(L,R,ls),s2=query(L,R,rs),s;
		s.val=s1.val+s2.val-(s1.rc==s2.lc);
		s.lc=s1.lc,s.rc=s2.rc;
		return s;
	}
}
void upd(int x,int y,int add) {
	while(top[x]!=top[y]) {
		if(dep[top[x]]<dep[top[y]]) swap(x,y);
		modify(dfn[top[x]],dfn[x],add,1);
		x=fa[top[x]];
	}
	if(dfn[x]>dfn[y]) swap(x,y);
	modify(dfn[x],dfn[y],add,1);
}

int get(int x,int y) {
	int ans=0,lc=0,rc=0;TREE s;
	while(top[x]!=top[y]) {
		if(dep[top[x]]<dep[top[y]]) swap(x,y),swap(lc,rc);//Qswap
		s=query(dfn[top[x]],dfn[x],1);
		ans+=s.val-(s.rc==lc);//Q
		lc=s.lc;
		x=fa[top[x]];
	}
	if(dfn[x]>dfn[y]) swap(x,y),swap(lc,rc);//Qswap
	s=query(dfn[x],dfn[y],1);
	return ans+s.val-(s.lc==lc)-(s.rc==rc);//Q
}

int main() {
	n=read();q=read();
	for(int i=1;i<=n;i++)
		a[i]=read();
	for(int i=2,x,y;i<=n;++i) {
		x=read();y=read();
		add(x,y),add(y,x);
	}
	dfs_son(1,0);
	dfs_chain(1,1);
	build(1,n,1); 

	char op;int a,b,c;
	while(q--) {
		cin>>op;a=read();b=read();
		if(op=='C') {
			c=read();
			upd(a,b,c);
		} else {
			printf("%d
",get(a,b));
		}
	}
	return 0;
}

LNOI2014LCA

这题统计 l~r 中所有点与z的lca的深度之和。

暴力就是把lca都求出来。。。显然这题没有暴力分

所以正解要么是一次求出多个lca不会,要么就是把这个求多个dep的和转化一下。

dep[i] 其实就是 i 到根有几个点

比较暴力的方法:我们不妨把 z 到根的路径上的点全部 +1,对于 l 到 r 之间的点询问他们到根路径上的点权和。上面的暴力具有叠加性,且可逆。也就是说我们可以对于 l 到 r 之间的点 i,将 i 到根的路径上的点全部 +1, 转而询问 z 到根的路径上的点(包括自身)的权值和就是这个询问的答案。

但是考虑到每次统计时,不能很好的排除 l ~ r 区间之外的点对 z->根 这条路径的贡献,所以我们每次都要清空线段树。

我们每次清空线段树,然后从 l ~ r 再添加一遍,树剖+线段树的复杂度就是 O(n * logn * logn)的,还要做 q 次,复杂度依然不理想。

把询问差分下,也就是用 [1, r] − [1, l − 1] 来计算答案,每个拆开的区间之间是有重叠的,是可以转移的,而不用每次都清空。

所以我们可以将差分后的区间按照端点从小到大排序(左端点都是根),然后按从小到大的顺序添加点,每遇到一个询问就查询一次,从而排除掉区间之外的点的影响,也就优化掉一个 q 的复杂度。

#include <queue>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>

using namespace std;
#define ls (p<<1)
#define rs (p<<1|1)
#define mid ((l+r)>>1)
const int N=100005; 
inline int read(){
	int x=0,f=1;char ch=getchar();
	while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
	while(isdigit(ch)){x=x*10+ch-'0';ch=getchar();}
	return f*x;
}

int n,m;
int to[N<<1],nxt[N<<1],hd[N],tot;
inline void add(int x,int y) {
	to[++tot]=y;nxt[tot]=hd[x];hd[x]=tot;
}
int siz[N],fa[N],son[N],dep[N];
void dfs_son(int x,int f) {
	siz[x]=1;fa[x]=f;dep[x]=dep[f]+1;
	for(int i=hd[x];i;i=nxt[i]) {
		int y=to[i];
		if(y==f)continue;
		dfs_son(y,x);
		siz[x]+=siz[y];
		if(siz[y]>siz[son[x]])son[x]=y;
	}
}
int dfn[N],rev[N],top[N],dfn_cnt;
void dfs_chain(int x,int tp) {
	top[x]=tp;
	dfn[x]=++dfn_cnt;rev[dfn_cnt]=x;
	if(son[x])dfs_chain(son[x],tp);
	for(int i=hd[x];i;i=nxt[i]) {
		int y=to[i];
		if(dfn[y])continue;
		dfs_chain(y,y);
	}
}

struct TREE{
    int len,sum,tag;
}t[N<<2];
struct node{
    int ans,z;
}q[N];
void build(int l,int r,int p) {
    t[p].len=r-l+1;
    if(l==r) return;
    build(l,mid,ls);
    build(mid+1,r,rs);
}
void pushdown(int p) {
    if(!t[p].tag) return;
    t[ls].tag+=t[p].tag;
    t[rs].tag+=t[p].tag;
    t[ls].sum+=t[p].tag*t[ls].len;
    t[rs].sum+=t[p].tag*t[rs].len;
    t[p].tag=0;
}
void modify(int l,int r,int L,int R,int p) {
    if(L<=l&&r<=R) {
        t[p].tag++;
        t[p].sum+=t[p].len;
        return;
    }
    pushdown(p);
    if(L<=mid) modify(l,mid,L,R,ls);
    if(R>mid) modify(mid+1,r,L,R,rs);
    t[p].sum=t[ls].sum+t[rs].sum;
}
void upd(int x,int y) {
    while(top[x]!=top[y]) {
        if(dep[top[x]]<dep[top[y]]) swap(x,y);
        modify(1,n,dfn[top[x]],dfn[x],1);
        x=fa[top[x]];
    }
    if(dfn[x]>dfn[y]) swap(x,y);
    modify(1,n,dfn[x],dfn[y],1);
}
int query(int l,int r,int L,int R,int p) {
    if(L<=l&&r<=R) return t[p].sum;
    pushdown(p);
    int ans=0;
    if(L<=mid) ans+=query(l,mid,L,R,ls);
    if(R>mid) ans+=query(mid+1,r,L,R,rs);
    return ans;
}
int getsum(int x,int y) {
    int ans=0;
    while(top[x]!=top[y]) {
        if(dep[top[x]]<dep[top[y]]) swap(x,y);
        ans+=query(1,n,dfn[top[x]],dfn[x],1);
        x=fa[top[x]];
    }
    if(dfn[x]>dfn[y]) swap(x,y);
    return ans+query(1,n,dfn[x],dfn[y],1);
}
struct QUES{
    int p,id;bool flag;
    QUES() {}
    QUES(int p_,int id_,bool flag_):p(p_),id(id_),flag(flag_){}
    bool operator < (const QUES &x) const {
        return p<x.p;
    }
}a[N];
int main(){
	n=read();m=read();
    for(int i=2,x;i<=n;i++) {
        x=read()+1;
        add(i,x);add(x,i);
    }
    dfs_son(1,0);
    dfs_chain(1,1);
    build(1,n,1);
    for(int i=1;i<=m;i++) {
        a[i*2-1]=QUES(read(),i,0);
        a[i*2]=QUES(read()+1,i,1);
        q[i].z=read()+1;
    }
    sort(a+1,a+1+2*m);
    int now=0;
    for(int i=1;i<=2*m;i++) {
        while(now<a[i].p) {
            now++;
            upd(1,now);
        }
        int r=getsum(1,q[a[i].id].z);
        q[a[i].id].ans+=a[i].flag?r:(-r);
    }
    for(int i=1;i<=m;i++)
        printf("%d
",q[i].ans%201314);
	return 0;
}

松鼠的新家

树剖写法(差分更简单)

#include <cstdio>
#include <iostream>
#include <vector>
using namespace std;
#define ls (p<<1)
#define rs (p<<1|1)
const int N=5e6;
inline int read(){
    int x=0;char ch=getchar();
    while(!isdigit(ch)) ch=getchar();
    while(isdigit(ch)) {x=x*10+ch-'0';ch=getchar();}
    return x;
}
int n,val[N],ans;
int to[N],nxt[N],hd[N],tot;
inline void add(int x,int y){
	to[++tot]=y;nxt[tot]=hd[x];hd[x]=tot;
}

int siz[N],dep[N],fa[N],son[N];
void dfs_son(int x,int f){
    siz[x]=1;fa[x]=f;dep[x]=dep[f]+1;
    for(int i=hd[x];i;i=nxt[i]){
        int y=to[i];
        if(y==f) continue;
        dfs_son(y,x);
        siz[x]+=siz[y];
        if(siz[y]>siz[son[x]]) son[x]=y;
    }
}
int dfn[N],rev[N],top[N],dfn_cnt;
void dfs_chain(int x,int tp){
    dfn[x]=++dfn_cnt;rev[dfn_cnt]=x;top[x]=tp;
    if(son[x]) dfs_chain(son[x],tp);
    for(int i=hd[x];i;i=nxt[i]){
        int y=to[i];
        if(dfn[y]) continue;
        dfs_chain(y,y);
    }
}

struct tree{
    int l,r;
    int tag,sum;
}t[N];
inline void push_up(int p){
    t[p].sum=(t[ls].sum+t[rs].sum);
}
void build(int l,int r,int p){
    t[p].l=l;t[p].r=r;
    if(l==r) return; 
    int mid=(l+r)>>1;
    build(l,mid,ls);
    build(mid+1,r,rs);
    push_up(p);
}
void push_down(int p){
    if(!t[p].tag) return;
    t[ls].tag+=t[p].tag;
    t[rs].tag+=t[p].tag;
    t[ls].sum+=(t[ls].r-t[ls].l+1)*t[p].tag;
    t[rs].sum+=(t[rs].r-t[rs].l+1)*t[p].tag;
    t[p].tag=0;
}
void modify(int L,int R,int v,int p){
    if(L<=t[p].l&&t[p].r<=R){
        t[p].tag+=v;
        t[p].sum+=(t[p].r-t[p].l+1)*v;
        return;
    }
    push_down(p);
    int mid=(t[p].l+t[p].r)>>1;
    if(L<=mid) modify(L,R,v,ls);
    if(R>mid) modify(L,R,v,rs);
    push_up(p);
}
int query(int L,int R,int p){
    if(L<=t[p].l&&t[p].r<=R) return t[p].sum;
    push_down(p);
    int mid=(t[p].l+t[p].r)>>1;
    int res=0;
    if(L<=mid) res+=query(L,R,ls);
    if(R>mid) res+=query(L,R,rs);
    return res;
}
void update(int x,int y,int v){
    while(top[x]!=top[y]){
        if(dep[top[x]]<dep[top[y]]) swap(x,y);
        modify(dfn[top[x]],dfn[x],v,1);
        x=fa[top[x]];
    }
    if(dfn[x]>dfn[y]) swap(x,y);
    modify(dfn[x],dfn[y],v,1);
}
int main(){
    n=read();
    for(int i=1;i<=n;i++) val[i]=read();
    for(int i=1,x,y;i<n;i++){
        x=read();y=read();
        add(x,y);add(y,x);
    }
    dfs_son(1,0);
    dfs_chain(1,1);
    build(1,n,1);
    for(int i=1;i<n;i++){
        int x=val[i],y=val[i+1];
        update(x,y,1);
        update(y,y,-1);
    }
    for(int i=1;i<=n;i++){
        ans=query(dfn[i],dfn[i],1);
        printf("%d
",ans);
    }
    return 0;
}
原文地址:https://www.cnblogs.com/ke-xin/p/13568212.html