【模板整合计划】图论—树论

【模板整合计划】图论—树论

一:【Prufer 序列】

【模板】 \(\text{Prufer}\) 序列

#include<algorithm>
#include<cstring>
#include<cstdio>
#define LL long long
#define Re register int
using namespace std;
const int N=5e6+3;
int n,op,fa[N],pf[N],son[N];LL ans;
inline void in(Re &x){
    int f=0;x=0;char c=getchar();
    while(c<'0'||c>'9')f|=c=='-',c=getchar();
    while(c>='0'&&c<='9')x=(x<<1)+(x<<3)+(c^48),c=getchar();
    x=f?-x:x;
}
inline LL get_prufer(){
    for(Re i=1;i<=n-1;++i)in(fa[i]),++son[fa[i]];
    for(Re i=1,j=1;i<=n-2;++i,++j){
        while(son[j])++j;pf[i]=fa[j];
        while(i<=n-2&&!--son[pf[i]]&&pf[i]<j)pf[i+1]=fa[pf[i]],++i;
    }
//    printf("Prufer: ");for(Re i=1;i<=n-2;++i)printf("%d ",pf[i]);puts("");
    for(Re i=1;i<=n-2;++i)ans^=(LL)i*pf[i];
    return ans;
}
inline LL get_fa(){
    for(Re i=1;i<=n-2;++i)in(pf[i]),++son[pf[i]];pf[n-1]=n;
    for(Re i=1,j=1;i<=n-1;++i,++j){
        while(son[j])++j;fa[j]=pf[i];
        while(i<=n-1&&!--son[pf[i]]&&pf[i]<j)fa[pf[i]]=pf[i+1],++i;
    }
//    printf("fa: ");for(Re i=1;i<=n-1;++i)printf("%d ",fa[i]);puts("");
    for(Re i=1;i<=n-1;++i)ans^=(LL)i*fa[i];
    return ans;
}
int main(){
    in(n),in(op),printf("%lld\n",op<2?get_prufer():get_fa());
}

二:【树的遍历】

1.【树的直径】

【模板】 \(\text{Longest path in a tree}\) \(\text{[SP1437]}\)

(1).【DFS】

const int N=1e4+3,M=3e5+3;
int n,m,x,y,o,head[N];
struct QAQ{int w,to,next;}a[N<<1];
inline void add(Re x,Re y,Re z){a[++o].w=z,a[o].to=y,a[o].next=head[x],head[x]=o;}
struct Tree_Diam_DFS{
    int cur,dis[N];
    inline void dfs(Re x,Re fa){
        if(dis[x]>dis[cur])cur=x;
        for(Re i=head[x],to;i;i=a[i].next)
            if((to=a[i].to)!=fa)dis[to]=dis[x]+a[i].w,dfs(to,x);
    }
    inline int sakura(){
        dfs(1,0);
        memset(dis,0,sizeof(dis));
        dfs(cur,0);
        return dis[cur];
    }
}T1;
int main(){
    in(n),m=n-1;
    while(m--)in(x),in(y),add(x,y,1),add(y,x,1);
    printf("%d\n",T1.sakura());
}

(2).【DP】

const int N=1e4+3,M=3e5+3;
int n,m,x,y,o,head[N];
struct QAQ{int w,to,next;}a[N<<1];
inline void add(Re x,Re y,Re z){a[++o].w=z,a[o].to=y,a[o].next=head[x],head[x]=o;}
struct Tree_Diam_DP1{
    int ans,dis[N];
    inline void dfs(Re x,Re fa){
        for(Re i=head[x],to,tmp;i;i=a[i].next)
            if((to=a[i].to)!=fa){
                dfs(to,x);
                ans=max(ans,dis[x]+dis[to]+a[i].w);
                dis[x]=max(dis[x],dis[to]+a[i].w);
            }
    }
    inline int sakura(){dfs(1,0);return ans;}
}T1;
struct Tree_Diam_DP2{
    int ans,dis[N][2];
    inline void dfs(Re x,Re fa){
        for(Re i=head[x],to,tmp;i;i=a[i].next)
            if((to=a[i].to)!=fa){
                dfs(to,x);
                if(dis[x][0]<(tmp=dis[to][0]+a[i].w))dis[x][1]=dis[x][0],dis[x][0]=tmp;
                else dis[x][1]=max(dis[x][1],tmp);
            }
        ans=max(ans,dis[x][1]+dis[x][0]);
    }
    inline int sakura(){dfs(1,0);return ans;}
}T2;
int main(){
    in(n),m=n-1;
    while(m--)in(x),in(y),add(x,y,1),add(y,x,1);
    printf("%d\n",T1.sakura());
//  printf("%d\n",T2.sakura());
}

2.【树的重心】

【模板】 \(\text{Balancing Act}\) \(\text{[Poj1655]}\)

const int N=2e4+3,inf=2e9;
int n,m,x,y,o,T,head[N];
struct QAQ{int w,to,next;}a[N<<1];
inline void add(Re x,Re y,Re z){a[++o].w=z,a[o].to=y,a[o].next=head[x],head[x]=o;}
struct Tree_Diam_DP1{
    int ans,dp[N],size[N];
    inline void dfs(Re x,Re fa){
        size[x]=1,dp[x]=0;
        for(Re i=head[x],to,tmp;i;i=a[i].next)
            if((to=a[i].to)!=fa){
                dfs(to,x),size[x]+=size[to];
                dp[x]=max(dp[x],size[to]);
            }
        if(n-size[x])dp[x]=max(dp[x],n-size[x]);
    }
    inline void sakura(){
        dfs(1,0),dp[ans=0]=inf;
        for(Re i=1;i<=n;++i)if(dp[i]<dp[ans])ans=i;
    }
}T1;
int main(){
    in(T);
    while(T--){
        memset(head,0,sizeof(head));
        in(n),m=n-1,o=0;
        while(m--)in(x),in(y),add(x,y,1),add(y,x,1);
        T1.sakura();
        printf("%d %d\n",T1.ans,T1.dp[T1.ans]);
    }
}

三:【树链剖分】

1.【树链剖分(重链剖分)】

【模板】 树链剖分 \(\text{[P3384]}\)

const int N=1e5+3;
int n,m,x,y,z,T,P,op,root,A[N],AA[N];
struct Killed_Tree{
    struct QAQ{int to,next;}a[N<<1];
    int o,id_o,fa[N],id[N],idx[N],top[N],son[N],size[N],deep[N],head[N];
    inline void add(Re x,Re y){a[++o].to=y,a[o].next=head[x],head[x]=o;}
    struct Segment_Tree{
        #define pl (p<<1)
        #define pr (p<<1|1)
        #define mid (L+R>>1)
        struct QAQ{int S,l,r,add;}tr[N<<2];
        inline void pushdown(Re p){
            Re v=tr[p].add;
            if(v){
                (tr[pl].S+=v*(tr[pl].r-tr[pl].l+1)%P)%=P,(tr[pl].add+=v)%=P;
                (tr[pr].S+=v*(tr[pr].r-tr[pr].l+1)%P)%=P,(tr[pr].add+=v)%=P;
                tr[p].add=0;
            }
        }
        inline void build(Re p,Re L,Re R){
            tr[p].l=L,tr[p].r=R;
            if(L==R){tr[p].S=AA[L]%P;return;}
            build(pl,L,mid),build(pr,mid+1,R);
            tr[p].S=(tr[pl].S+tr[pr].S)%P;
        }
        inline void change(Re p,Re l,Re r,Re v){
            Re L=tr[p].l,R=tr[p].r;
            if(l<=L&&R<=r){(tr[p].S+=v*(R-L+1)%P)%=P,(tr[p].add+=v)%=P;return;}
            pushdown(p);
            if(l<=mid)change(pl,l,r,v);
            if(r>mid)change(pr,l,r,v);
            tr[p].S=(tr[pl].S+tr[pr].S)%P;
        }
        inline int ask(Re p,Re l,Re r){
            Re L=tr[p].l,R=tr[p].r;
            if(l<=L&&R<=r)return tr[p].S;
            Re ans=0;pushdown(p);
            if(l<=mid)(ans+=ask(pl,l,r))%=P;
            if(r>mid)(ans+=ask(pr,l,r))%=P;
            return ans;
        }
    }TR;
    inline void dfs1(Re x,Re Fa){
        size[x]=1,deep[x]=deep[fa[x]=Fa]+1;
        for(Re i=head[x],to;i;i=a[i].next)
            if((to=a[i].to)!=Fa){
                dfs1(to,x);
                size[x]+=size[to];
                if(size[to]>size[son[x]])son[x]=to;
            }
    }
    inline void dfs2(Re x,Re rt){
        id[x]=++id_o,idx[id_o]=x,AA[id_o]=A[x],top[x]=rt;
        if(!son[x])return;//叶节点
        dfs2(son[x],rt);
        for(Re i=head[x],to;i;i=a[i].next)
            if((to=a[i].to)!=fa[x]&&to!=son[x])dfs2(to,to);
    }
    inline void kill(Re rt){dfs1(rt,0),dfs2(rt,rt),TR.build(1,1,n);}
    inline int ask_dis(Re x,Re y){//询问x到y的简单路径上权值之和
        Re ans=0;
        while(top[x]!=top[y]){
            if(deep[top[x]]<deep[top[y]])swap(x,y);
            (ans+=TR.ask(1,id[top[x]],id[x]))%=P;
            x=fa[top[x]];
        }
        if(deep[x]>deep[y])swap(x,y);
        (ans+=TR.ask(1,id[x],id[y]))%=P;
        return ans;
    }
    inline void change_dis(Re x,Re y,Re v){//x到y的简单路径上权值加上v
        while(top[x]!=top[y]){
            if(deep[top[x]]<deep[top[y]])swap(x,y);
            TR.change(1,id[top[x]],id[x],v);
            x=fa[top[x]];
        }
        if(deep[x]>deep[y])swap(x,y);
        TR.change(1,id[x],id[y],v);
    }
    inline int ask_son(Re x){//询问以x为根的子树权值之和
        return TR.ask(1,id[x],id[x]+size[x]-1);
    }
    inline void change_son(Re x,Re v){//以x为根的子树权值加上v
        TR.change(1,id[x],id[x]+size[x]-1,v);
    }
}T1;
int main(){
    in(n),in(T),in(root),in(P),m=n-1;
    for(Re i=1;i<=n;++i)in(A[i]);
    while(m--)in(x),in(y),T1.add(x,y),T1.add(y,x);
    T1.kill(root);
    while(T--){
        in(op);
        if(op<2)in(x),in(y),in(z),T1.change_dis(x,y,z%P);
        else if(op<3)in(x),in(y),printf("%d\n",T1.ask_dis(x,y));
        else if(op<4)in(x),in(z),T1.change_son(x,z%P);
        else in(x),printf("%d\n",T1.ask_son(x));
    }
}

2.【长链剖分】

【模板】 \(\text{Dominant Indices}\) \(\text{[CF1009F]}\)

const int N=1e6+3;
int n,m,x,y,o,Sz[N],son[N],Ans[N],dep[N],head[N];int *SZ=Sz,*dp[N];
struct QAQ{int to,next;}a[N<<1];
inline void add(Re x,Re y){a[++o].to=y,a[o].next=head[x],head[x]=o;}
inline void dfs1(Re x,Re fa){
    for(Re i=head[x],to;i;i=a[i].next)
        if((to=a[i].to)!=fa){
            dfs1(to,x);
            if(dep[to]>dep[son[x]])son[x]=to;
        }
    dep[x]=dep[son[x]]+1;
}
inline void dfs2(Re x,Re fa){
    dp[x][0]=1;
    if(son[x])dp[son[x]]=dp[x]+1,dfs2(son[x],x),Ans[x]=Ans[son[x]]+1;
    for(Re i=head[x],to;i;i=a[i].next)
        if((to=a[i].to)!=fa&&to!=son[x]){
            dp[to]=SZ,SZ+=dep[to],dfs2(to,x);
            for(Re j=1;j<=dep[to];++j){
                dp[x][j]+=dp[to][j-1];
                if(dp[x][j]>dp[x][Ans[x]])Ans[x]=j;
                else if(dp[x][j]==dp[x][Ans[x]])Ans[x]=min(Ans[x],j);
            }
        }
    if(dp[x][Ans[x]]==1)Ans[x]=0;
}
int main(){
//    freopen("123.txt","r",stdin);
    in(n),m=n-1;
    while(m--)in(x),in(y),add(x,y),add(y,x);
    dfs1(1,0),dp[1]=SZ,SZ+=dep[1],dfs2(1,0);
    for(Re i=1;i<=n;++i)printf("%d\n",Ans[i]);
}

【模板】 \(\text{Link Cut Tree}\)(动态树)\(\text{[P3690]}\)

const int N=1e5+3;
int n,x,y,T,op,a[N];
struct Link_Cut_Tree{
    #define pl tr[p].ps[0]
    #define pr tr[p].ps[1]
    #define pf tr[p].fa
    int Q[N];
    struct QAQ{int fa,ans,tag,ps[2];}tr[N];
    inline void pushup(Re p){
        tr[p].ans=a[p]^tr[pl].ans^tr[pr].ans;
    }
    inline void updata(Re p){swap(pl,pr),tr[p].tag^=1;}
    inline void pushdown(Re p){
        if(tr[p].tag){
            if(pl)updata(pl);
            if(pr)updata(pr);
            tr[p].tag=0;
        }
    }
    inline int nort(Re p){return tr[pf].ps[0]==p||tr[pf].ps[1]==p;}
    inline int which(Re p){return tr[pf].ps[1]==p;}
    inline void connect(Re p,Re fa,Re o){tr[pf=fa].ps[o]=p;}
    inline void rotate(Re p){
        Re fa=pf,fas=which(p);
        Re pa=tr[fa].fa,pas=which(fa);
        Re x=tr[p].ps[fas^1];
        if(nort(fa))tr[pa].ps[pas]=p;pf=pa;
        connect(x,fa,fas),connect(fa,p,fas^1);
        pushup(fa),pushup(p);
    }
    inline void splay(Re p){
        Re x=p,t=0;Q[++t]=x;
        while(nort(x))Q[++t]=x=tr[x].fa;
        while(t)pushdown(Q[t--]);
        for(Re fa;nort(p);rotate(p))
            if(nort(fa=pf))rotate(which(p)==which(fa)?fa:p);
    }
    inline void access(Re p){
        for(Re son=0;p;son=p,p=pf)
            splay(p),pr=son,pushup(p);
    }
    inline void makeroot(Re p){access(p),splay(p),updata(p);}
    inline int findroot(Re p){
        access(p),splay(p),pushdown(p);
        while(pl)pushdown(p=pl);
        return p;
    }
    inline void split(Re x,Re y){makeroot(x),access(y),splay(y);}
    inline void link(Re x,Re y){makeroot(x);if(findroot(y)!=x)tr[x].fa=y;}
    inline void cut(Re x,Re y){
        makeroot(x);
        if(findroot(y)==x&&tr[x].fa==y&&!tr[x].ps[1])
            tr[x].fa=tr[y].ps[0]=0,pushup(y);
    }
}LCT;
int main(){
    in(n),in(T);
    for(Re i=1;i<=n;++i)in(a[i]);
    while(T--){
        in(op),in(x),in(y);
        if(!op)LCT.split(x,y),printf("%d\n",LCT.tr[y].ans);
        else if(op<2)LCT.link(x,y);
        else if(op<3)LCT.cut(x,y);
        else LCT.splay(x),a[x]=y,LCT.pushup(x);
    }
}

(2).【LCT 维护虚子树信息】

大融合 \(\text{[BJOI2014] [P4219]}\)

const int N=1e5+3;
int n,x,y,T;char op;
struct Link_Cut_Tree{
    #define pl tr[p].ps[0]
    #define pr tr[p].ps[1]
    #define pf tr[p].fa
    int Q[N];
    struct QAQ{int fa,si,ans,tag,ps[2];}tr[N];
    inline void pushup(Re p){
        tr[p].ans=1+tr[p].si+tr[pl].ans+tr[pr].ans;
    }
    inline void updata(Re p){swap(pl,pr),tr[p].tag^=1;}
    inline void pushdown(Re p){
        if(tr[p].tag){
            if(pl)updata(pl);
            if(pr)updata(pr);
            tr[p].tag=0;
        }
    }
    inline int nort(Re p){return tr[pf].ps[0]==p||tr[pf].ps[1]==p;}
    inline int which(Re p){return tr[pf].ps[1]==p;}
    inline void connect(Re p,Re fa,Re o){tr[pf=fa].ps[o]=p;}
    inline void rotate(Re p){
        Re fa=pf,fas=which(p);
        Re pa=tr[fa].fa,pas=which(fa);
        Re x=tr[p].ps[fas^1];
        if(nort(fa))tr[pa].ps[pas]=p;pf=pa;
        connect(x,fa,fas),connect(fa,p,fas^1);
        pushup(fa),pushup(p);
    }
    inline void splay(Re p){
        Re x=p,t=0;Q[++t]=x;
        while(nort(x))Q[++t]=x=tr[x].fa;
        while(t)pushdown(Q[t--]);
        for(Re fa;nort(p);rotate(p))
            if(nort(fa=pf))rotate(which(p)==which(fa)?fa:p);
    }
    inline void access(Re p){
        for(Re son=0;p;son=p,p=pf)
            splay(p),tr[p].si+=tr[pr].ans,tr[p].si-=tr[pr=son].ans,pushup(p);
    }
    inline void makeroot(Re p){access(p),splay(p),updata(p);}
    inline int findroot(Re p){
        access(p),splay(p),pushdown(p);
        while(pl)pushdown(p=pl);
        return p;
    }
    inline void split(Re x,Re y){makeroot(x),access(y),splay(y);}
    inline void link(Re x,Re y){
        makeroot(x);
        if(findroot(y)!=x)tr[tr[x].fa=y].si+=tr[x].ans,pushup(y);
    }
    inline void cut(Re x,Re y){
        makeroot(x);
        if(findroot(y)==x&&tr[x].fa==y&&!tr[x].ps[1])
            tr[x].fa=tr[y].ps[0]=0,pushup(y);
    }
    inline int ask(Re x,Re y){split(x,y);return (tr[x].si+1)*(tr[y].si+1);}
}LCT;
int main(){
    in(n),in(T);
    while(T--){
        scanf(" %c",&op),in(x),in(y);
        if(op=='A')LCT.link(x,y);
        if(op=='Q')printf("%d\n",LCT.ask(x,y));
    }
}

(3).【LCT 维护原子树信息】

遥远的国度 \(\text{[P3979]}\)

const int N=1e5+3,inf=2e9;
int n,x,y,z,T,op,root,X[N],Y[N];
struct Link_Cut_Tree{
    #define pl tr[p].ps[0]
    #define pr tr[p].ps[1]
    #define pf tr[p].fa
    #define pv tr[p].v
    int Q[N];
    struct QAQ{int v,fa,si,shi,ans,add,tag,ans0,ps[2];multiset<int>SI;}tr[N];
    //shi: 所有实儿子(在该链上的v)
    //si: 子树内所有虚儿子(不在该链上的ans)
    inline void pushup(Re p){
        tr[p].shi=min(pv,min(tr[pl].shi,tr[pr].shi));//只要实的
        tr[p].si=min(*tr[p].SI.begin(),min(tr[pl].si,tr[pr].si));//只要虚的
        tr[p].ans=min(tr[p].shi,tr[p].si);
    }
    inline void updata(Re p){tr[p].tag^=1,swap(pl,pr);}
    inline void updata(Re p,Re v){tr[p].add=1,tr[p].shi=pv=v,tr[p].ans=min(tr[p].shi,tr[p].si);}//只修改实的,虚的不变
    inline void pushdown(Re p){
        if(tr[p].tag){
            if(pl)updata(pl);
            if(pr)updata(pr);
            tr[p].tag=0;
        }
        if(tr[p].add){
            if(pl)updata(pl,pv);
            if(pr)updata(pr,pv);
            tr[p].add=0;
        }
    }
    inline int which(Re p){return tr[pf].ps[1]==p;}
    inline int nort(Re p){return tr[pf].ps[0]==p||tr[pf].ps[1]==p;}
    inline void connect(Re p,Re fa,Re o){tr[pf=fa].ps[o]=p;}
    inline void rotate(Re p){
        Re fa=pf,fas=which(p);
        Re pa=tr[fa].fa,pas=which(fa);
        Re x=tr[p].ps[fas^1];
        if(nort(fa))tr[pa].ps[pas]=p;pf=pa;
        connect(x,fa,fas),connect(fa,p,fas^1);
        pushup(fa),pushup(p);
    }
    inline void splay(Re p){
        Re x=p,t=0;Q[++t]=x;
        while(nort(x))Q[++t]=x=tr[x].fa;
        while(t)pushdown(Q[t--]);
        for(Re fa;nort(p);rotate(p))
            if(nort(fa=pf))rotate(which(p)==which(fa)?fa:p);
    }
    inline void access(Re p){
        for(Re son=0;p;son=p,p=pf){
            splay(p);
            if(pr)tr[p].SI.insert(tr[pr].ans);
            pr=son;
            if(pr)tr[p].SI.erase(tr[p].SI.lower_bound(tr[pr].ans));
            pushup(p);
        }
    }
    inline void makeroot(Re p){access(p),splay(p),updata(p);}
    inline void link(Re x,Re y){makeroot(x),access(y),splay(y),tr[tr[x].fa=y].SI.insert(tr[x].ans);}
    inline void change(Re x,Re y,Re z){makeroot(x),access(y),splay(y),updata(y,z);}
    inline int ask_son(Re p){access(p),splay(p);return min(pv,*tr[p].SI.begin());}
}T1;
int main(){
    in(n),in(T),T1.tr[0].shi=T1.tr[0].si=inf;
    for(Re i=1;i<n;++i)in(X[i]),in(Y[i]);
    for(Re i=1;i<=n;++i)in(T1.tr[i].v),T1.tr[i].SI.insert(inf),T1.pushup(i);
    for(Re i=1;i<n;++i)T1.link(X[i],Y[i]);
    in(root),T1.makeroot(root);
    while(T--){
        in(op),in(x);
        if(op<2)T1.makeroot(root=x);
        else if(op<3)in(y),in(z),T1.change(x,y,z),T1.makeroot(root);
        else printf("%d\n",T1.ask_son(x));
    }
}

(4).【LCT 动态维护最小生成树】

水管局长 \(\text{[WC2006] [P4172]}\)

const int N=1003,M=1e5+3;
int n,m,x,y,z,T,op,id,id_O,v[N+M],vis[N+M],Ans[M],ID[N][N];
struct Query{int op,x,y;}Q[M];
struct Link_Cut_Tree{
    #define pl tr[p].ps[0]
    #define pr tr[p].ps[1]
    #define pf tr[p].fa
    int Q[N+M];
    struct QAQ{int mx,fa,tag,ps[2];}tr[N+M];
    inline void pushup(Re p){
        tr[p].mx=p;
        if(pl&&v[tr[pl].mx]>v[tr[p].mx])tr[p].mx=tr[pl].mx;
        if(pr&&v[tr[pr].mx]>v[tr[p].mx])tr[p].mx=tr[pr].mx;
    }
    inline void updata(Re p){swap(pl,pr),tr[p].tag^=1;}
    inline void pushdown(Re p){
        if(tr[p].tag){
            if(pl)updata(pl);
            if(pr)updata(pr);
            tr[p].tag=0;
        }
    }
    inline int which(Re p){return tr[pf].ps[1]==p;}
    inline int nort(Re p){return tr[pf].ps[0]==p||tr[pf].ps[1]==p;}
    inline void connect(Re p,Re fa,Re o){tr[pf=fa].ps[o]=p;}
    inline void rotate(Re p){
        Re fa=pf,fas=which(p);
        Re pa=tr[fa].fa,pas=which(fa);
        Re x=tr[p].ps[fas^1];
        if(nort(fa))tr[pa].ps[pas]=p;pf=pa;
        connect(x,fa,fas),connect(fa,p,fas^1);
        pushup(fa),pushup(p);
    }
    inline void splay(Re p){
        Re x=p,t=0;Q[++t]=x;
        while(nort(x))Q[++t]=x=tr[x].fa;
        while(t)pushdown(Q[t--]);
        for(Re fa;nort(p);rotate(p))
            if(nort(fa=pf))rotate(which(p)==which(fa)?fa:p);
    }
    inline void access(Re p){
        for(Re son=0;p;son=p,p=pf)
            splay(p),pr=son,pushup(p);
    }
    inline void makeroot(Re x){access(x),splay(x),updata(x);}
    inline int findroot(Re p){
        access(p),splay(p),pushdown(p);
        while(pl)pushdown(p=pl);
        return p;
    }
    inline void split(Re x,Re y){makeroot(x),access(y),splay(y);}
    inline void link(Re x,Re y){makeroot(x);if(findroot(y)!=x)tr[x].fa=y;}
    inline void cut(Re x,Re y){
        makeroot(x);
        if(findroot(y)==x&&tr[x].fa==y&&!tr[x].ps[1])
            tr[x].fa=tr[y].ps[0]=0,pushup(y);
    }
    inline void MST_link(Re x,Re y,Re id){
        makeroot(x);
        if(findroot(y)!=x)link(x,id),link(id,y);
        else{
            split(x,y);Re mx=tr[y].mx;
            if(v[id]<v[mx]){
                splay(mx);
                tr[tr[mx].ps[0]].fa=tr[tr[mx].ps[1]].fa=0;
                tr[mx].ps[0]=tr[mx].ps[1]=tr[mx].fa=0;
                link(x,id),link(id,y);
            }
        }
    }
    inline int MST_path(Re x,Re y){split(x,y);return v[tr[y].mx];}
}LCT;
struct Kruscal{
    int fa[N];
    struct QAQ{int x,y,z;inline bool operator<(const QAQ &O)const{return z<O.z;}}a[M];
    inline int find(Re x){return x==fa[x]?x:fa[x]=find(fa[x]);}
    inline void sakura(){
        for(Re i=1;i<=n;++i)fa[i]=i;
        for(Re i=1;i<=m;++i)in(x),in(y),in(z),v[ID[x][y]=ID[y][x]=++id_O+n]=z,a[i]=(QAQ){x,y,z};
        for(Re i=1;i<=T;++i)in(op),in(x),in(y),vis[ID[x][y]]|=op>1,Q[i]=(Query){op,x,y};
        sort(a+1,a+m+1);
        for(Re i=1,t=0;t<n-1&&i<=m;++i)
            if(!vis[ID[x=a[i].x][y=a[i].y]]&&find(x)!=find(y))
                fa[find(x)]=find(y),++t,LCT.MST_link(x,y,ID[x][y]);
    }
}T1;
int main(){
    in(n),in(m),in(T),T1.sakura();
    for(Re i=T;i>=1;--i){
        Re op=Q[i].op,x=Q[i].x,y=Q[i].y;
        if(op&1)Ans[++Ans[0]]=LCT.MST_path(x,y);
        else LCT.MST_link(x,y,ID[x][y]);
    }
    for(Re i=Ans[0];i>=1;--i)printf("%d\n",Ans[i]);
}

4.【动态 DP (DDP)】

【模板】 动态 \(\text{DP [P4719]}\)

(1).【树链剖分】

#define Mat Matrix
const int N=1e5+3;
const LL inf=1e18;
int n,m,o,x,y,T,A[N],head[N];
struct QAQ{int to,next;}a[N<<1];
inline void add(Re x,Re y){a[++o].to=y,a[o].next=head[x],head[x]=o;}
struct Matrix{
    LL a[3][3];Mat(){a[1][1]=a[1][2]=a[2][1]=a[2][2]=-inf;}
    inline Mat operator*(const Mat &O)const{
        Mat A;
        for(Re i=1;i<=2;++i)
            for(Re j=1;j<=2;++j)
                for(Re k=1;k<=2;++k)
                    A.a[i][j]=max(A.a[i][j],a[i][k]+O.a[k][j]);
        return A;
    }
}G[N];
int idx[N];
struct killed_Tree{
    struct Segment_Tree{
        #define pl (p<<1)
        #define pr (p<<1|1)
        #define mid ((L+R)>>1)
        Mat tr[N<<2];
        inline void pushup(Re p){tr[p]=tr[pl]*tr[pr];}
        inline void build(Re p,Re L,Re R){
            if(L==R){tr[p]=G[idx[L]];return;}
            build(pl,L,mid),build(pr,mid+1,R);
            pushup(p);
        }
        inline void change(Re p,Re L,Re R,Re x,Mat &v){
            if(L==R){tr[p]=v;return;}
            if(x<=mid)change(pl,L,mid,x,v);
            else change(pr,mid+1,R,x,v);
            pushup(p);
        }
        inline Mat ask(Re p,Re L,Re R,Re l,Re r){
            if(l<=L&&R<=r)return tr[p];
            if(r<=mid)return ask(pl,L,mid,l,r);
            else if(l>mid)return ask(pr,mid+1,R,l,r);
            else return ask(pl,L,mid,l,r)*ask(pr,mid+1,R,l,r);
        }
    }TR;
    int id_O,fa[N],st[N],ed[N],son[N],top[N],deep[N],size[N];LL f[N][2],g[N][2];
    inline void dfs1(Re x,Re Fa){
        deep[x]=deep[fa[x]=Fa]+1,size[x]=1;
        for(Re i=head[x],to;i;i=a[i].next)
            if((to=a[i].to)!=Fa){
                dfs1(to,x),size[x]+=size[to];
                if(size[to]>size[son[x]])son[x]=to;
            }
    }
    inline void dfs2(Re x,Re rt){//在剖树时顺手把初始的答案数组f,g求出来
        idx[st[x]=++id_O]=x,ed[rt]=max(ed[rt],id_O),top[x]=rt;
        f[x][0]=g[x][0]=0,f[x][1]=g[x][1]=A[x];//f:包含所有儿子的答案。g:只包含轻儿子的答案。
        if(son[x])dfs2(son[x],rt),f[x][0]+=max(f[son[x]][0],f[son[x]][1]),f[x][1]+=f[son[x]][0];
        for(Re i=head[x],to;i;i=a[i].next)
            if((to=a[i].to)!=fa[x]&&to!=son[x]){
                dfs2(to,to);
                f[x][0]+=max(f[to][0],f[to][1]),f[x][1]+=f[to][0];
                g[x][0]+=max(f[to][0],f[to][1]),g[x][1]+=f[to][0];
            }
        G[x].a[1][1]=G[x].a[1][2]=g[x][0];
        G[x].a[2][1]=g[x][1],G[x].a[2][2]=-inf;
    }
    inline LL ask(){Mat F=TR.ask(1,1,n,st[1],ed[1]);return max(F.a[1][1],F.a[2][1]);}//询问答案(注意这里取min时应该是看F中的情况而不是G,且注F的摆放位置应满足叶子节点处与G中对应值相同)
    inline void change(Re x,Re v){//修改x的点权
        G[x].a[2][1]+=v-A[x],A[x]=v;//修改G[x][1]
        while(x){
            Mat F1=TR.ask(1,1,n,st[top[x]],ed[top[x]]);//这条链修改之前的f数组
            TR.change(1,1,n,st[x],G[x]);//修改点x的G
            Mat F2=TR.ask(1,1,n,st[top[x]],ed[top[x]]);//这条链修改之后的f数组
            x=fa[top[x]];//跳到下一条链
            G[x].a[1][1]+=max(F2.a[1][1],F2.a[2][1])-max(F1.a[1][1],F1.a[2][1]),G[x].a[1][2]=G[x].a[1][1];
            G[x].a[2][1]+=F2.a[1][1]-F1.a[1][1];//根据转移方程进行更新
        }
    }
}T1;
int main(){
//    freopen("123.txt","r",stdin);
    in(n),in(T),m=n-1;
    for(Re i=1;i<=n;++i)in(A[i]);
    while(m--)in(x),in(y),add(x,y),add(y,x);
    T1.dfs1(1,0),T1.dfs2(1,1),T1.TR.build(1,1,n);
    while(T--)in(x),in(y),T1.change(x,y),printf("%lld\n",T1.ask());
}

(2).【LCT】

#define Mat Matrix
const int N=1e5+3;
const LL inf=1e18;
int n,m,o,x,y,T,A[N],head[N];
struct QAQ{int to,next;}a[N<<1];
inline void add(Re x,Re y){a[++o].to=y,a[o].next=head[x],head[x]=o;}
struct Matrix{
    LL a[3][3];Mat(){a[1][1]=a[1][2]=a[2][1]=a[2][2]=-inf;}
    inline Mat operator*(const Mat &O)const{
        Mat A;
        for(Re i=1;i<=2;++i)
            for(Re j=1;j<=2;++j)
                for(Re k=1;k<=2;++k)
                    A.a[i][j]=max(A.a[i][j],a[i][k]+O.a[k][j]);
        return A;
    }
}G[N];
struct Link_Cut_Tree{
    #define pl tr[p].ps[0]
    #define pr tr[p].ps[1]
    #define pf tr[p].fa
    struct QAQ{int fa,ps[2];}tr[N];Mat TR[N];
    inline void pushup(Re p){
        TR[p]=G[p];
        if(pl)TR[p]=TR[pl]*TR[p];
        if(pr)TR[p]=TR[p]*TR[pr];
    }
    inline int which(Re p){return tr[pf].ps[1]==p;}
    inline int nort(Re p){return tr[pf].ps[0]==p||tr[pf].ps[1]==p;}
    inline void connect(Re p,Re fa,Re o){tr[pf=fa].ps[o]=p;}
    inline void rotate(Re p){
        Re fa=pf,fas=which(p);
        Re pa=tr[fa].fa,pas=which(fa);
        Re x=tr[p].ps[fas^1];
        if(nort(fa))tr[pa].ps[pas]=p;pf=pa;
        connect(x,fa,fas),connect(fa,p,fas^1);
        pushup(fa),pushup(p);
    }
    inline void splay(Re p){
        for(Re fa;nort(p);rotate(p))
            if(nort(fa=pf))rotate(which(p)==which(fa)?fa:p);
    }
    inline void access(Re p){
        for(Re son=0;p;son=p,p=pf){
            splay(p);
            if(pr)G[p].a[1][1]+=max(TR[pr].a[1][1],TR[pr].a[2][1]),G[p].a[1][2]=G[p].a[1][1],G[p].a[2][1]+=TR[pr].a[1][1];
            pr=son;
            if(pr)G[p].a[1][1]-=max(TR[pr].a[1][1],TR[pr].a[2][1]),G[p].a[1][2]=G[p].a[1][1],G[p].a[2][1]-=TR[pr].a[1][1];
            pushup(p);
        }
    }
    inline void change(Re x,Re v){access(x),splay(x),G[x].a[2][1]+=v-A[x],A[x]=v,pushup(x);}//修改x的点权(G[x][1])
    inline LL ask(){splay(1);return max(TR[1].a[1][1],TR[1].a[2][1]);}//询问答案(注意这里取min时应该是看F中的情况而不是G,且注F的摆放位置应满足无实儿子节点处与G中对应值相同)
}T1;
LL g[N][2];//初始时CLT上全为轻边,刚好可以直接求g
inline void dfs(Re x,Re fa){//在剖树时顺手把初始的答案数组g求出来
    g[x][1]=A[x],T1.tr[x].fa=fa;
    for(Re i=head[x],to;i;i=a[i].next)if((to=a[i].to)!=fa)
        dfs(to,x),g[x][0]+=max(g[to][0],g[to][1]),g[x][1]+=g[to][0];
    G[x].a[1][1]=G[x].a[1][2]=g[x][0];
    G[x].a[2][1]=g[x][1],G[x].a[2][2]=-inf;
    T1.pushup(x);
}
int main(){
    in(n),in(T),m=n-1;
    for(Re i=1;i<=n;++i)in(A[i]);
    while(m--)in(x),in(y),add(x,y),add(y,x);
    dfs(1,0);
    while(T--)in(x),in(y),T1.change(x,y),printf("%lld\n",T1.ask());
}

(3).【全局平衡二叉树 (AVL)】


四:【最近公共祖先 (LCA)】

【模板】 最近公共祖先(\(\text{LCA}\)\(\text{[P3379]}\)

1.【树上倍增】

const int N=5e5+3,M=5e5+3,logN=18;//注意考虑最多一次跳\floor(\log N)级
struct LCA{
	int deep[N],ant[N][logN+1];
	inline void dfs(Re x,Re fa){
		deep[x]=deep[ant[x][0]=fa]+1;
		for(Re i=1;(1<<i)<=deep[x];++i)ant[x][i]=ant[ant[x][i-1]][i-1];
		for(Re i=head[x];i;i=a[i].next)if(a[i].to!=fa)dfs(a[i].to,x);
	}
	inline int lca(Re x,Re y){
		if(deep[x]<deep[y])swap(x,y);//先把x变为深度更大的点
		for(Re i=logN;i>=0;--i)if(deep[ant[x][i]]>=deep[y])x=ant[x][i];//x单独往上跳到深度与y相等的位置
		if(x==y)return x;//如果已经相遇了就返回
		for(Re i=logN;i>=0;--i)if(ant[x][i]!=ant[y][i])x=ant[x][i],y=ant[y][i];//如果往上跳2^i步都还没有相遇
		return ant[x][0];//最后一步到达
	}
}T1;

2.【欧拉序+ST 表】

const int N=5e5+3,logN=19;
struct LCA{
	int O,lg[N<<1],Qx[N<<1],Qd[N<<1],fir[N],deep[N],f[N<<1][logN+1],ans[N<<1][logN+1];
	inline void dfs(Re x,Re fa){
		Qx[fir[x]=++O]=x,Qd[O]=deep[x]=deep[fa]+1;//加x加入欧拉序列
		for(Re i=head[x],to;i;i=a[i].next)
			if((to=a[i].to)!=fa)dfs(to,x),Qx[++O]=x,Qd[O]=deep[x];//加x加入欧拉序列
	}
	inline void build(Re rt){
		dfs(rt,0),lg[0]=-1;
		for(Re i=1;i<=O;++i)f[i][0]=Qd[i],ans[i][0]=Qx[i],lg[i]=lg[i>>1]+1;
		for(Re j=1;j<=logN;++j)
			for(Re i=1;i+(1<<j)-1<=O;++i)
				if(f[i][j-1]<f[i+(1<<j-1)][j-1])f[i][j]=f[i][j-1],ans[i][j]=ans[i][j-1];
				else f[i][j]=f[i+(1<<j-1)][j-1],ans[i][j]=ans[i+(1<<j-1)][j-1];
	}
	inline int lca(Re x,Re y){
		Re L=fir[x],R=fir[y];
		if(L>R)swap(L,R);
		Re m=lg[R-L+1];
		return f[L][m]<f[R-(1<<m)+1][m]?ans[L][m]:ans[R-(1<<m)+1][m];
	}
}T1;

3.【Tarjan】

咕咕咕。

4.【树链剖分】

【模板】 最近公共祖先(\(\text{LCA}\)\(\text{[P3379]}\)

const int N=5e5+3;
int n,m,x,y,T,root;
struct Killed_Tree{
    struct QAQ{int to,next;}a[N<<1];
    int o,head[N];
    inline void add(Re x,Re y){a[++o].to=y,a[o].next=head[x],head[x]=o;}
    int id_o,id[N],fa[N],son[N],top[N],deep[N],size[N];
    inline void dfs1(Re x,Re Fa){
        size[x]=1,deep[x]=deep[fa[x]=Fa]+1;
        for(Re i=head[x],to;i;i=a[i].next)
            if((to=a[i].to)!=Fa){
                dfs1(to,x);
                size[x]+=size[to];
                if(size[to]>size[son[x]])son[x]=to;
            }
    }
    inline void dfs2(Re x,Re rt){
        id[x]=++id_o,top[x]=rt;
        if(!son[x])return;
        dfs2(son[x],rt);
        for(Re i=head[x],to;i;i=a[i].next)
            if((to=a[i].to)!=fa[x]&&to!=son[x])dfs2(to,to);
    }
    inline void kill(Re rt){dfs1(rt,0),dfs2(rt,rt);}
    inline int lca(Re x,Re y){
        while(top[x]!=top[y]){
            if(deep[top[x]]<deep[top[y]])swap(x,y);
            x=fa[top[x]];
        }
        if(deep[x]>deep[y])swap(x,y);
        return x;
    }
}T1;
int main(){
    in(n),in(T),in(root),m=n-1;
    while(m--)in(x),in(y),T1.add(x,y),T1.add(y,x);
    T1.kill(root);
    while(T--)in(x),in(y),printf("%d\n",T1.lca(x,y));
}

5.【LCT】

const int N=5e5+3;
int n,m,x,y,T,root;
struct Link_Cut_Tree{
    #define pl tr[p].ps[0]
    #define pr tr[p].ps[1]
    #define pf tr[p].fa
    int Q[N];
    struct QAQ{int fa,tag,ps[2];}tr[N];
    inline void pushup(Re p){}
    inline void updata(Re p){swap(pl,pr),tr[p].tag^=1;}
    inline void pushdown(Re p){
        if(tr[p].tag){
            if(pl)updata(pl);
            if(pr)updata(pr);
            tr[p].tag=0;
        }
    }
    inline int nort(Re p){return tr[pf].ps[0]==p||tr[pf].ps[1]==p;}
    inline int which(Re p){return tr[pf].ps[1]==p;}
    inline void connect(Re p,Re fa,Re o){tr[pf=fa].ps[o]=p;}
    inline void rotate(Re p){
        Re fa=pf,fas=which(p);
        Re pa=tr[fa].fa,pas=which(fa);
        Re x=tr[p].ps[fas^1];
        if(nort(fa))tr[pa].ps[pas]=p;pf=pa;
        connect(x,fa,fas),connect(fa,p,fas^1);
        pushup(fa),pushup(p);
    }
    inline void splay(Re p){
        Re x=p,t=0;Q[++t]=x;
        while(nort(x))Q[++t]=x=tr[x].fa;
        while(t)pushdown(Q[t--]);
        for(Re fa;nort(p);rotate(p))
            if(nort(fa=pf))rotate(which(p)==which(fa)?fa:p);
    }
    inline int access(Re p){
        Re son=0;
        for(;p;son=p,p=pf)
            splay(p),pr=son,pushup(p);
        return son;
    }
    inline void makeroot(Re p){access(p),splay(p),updata(p);}
    inline int findroot(Re p){
        access(p),splay(p),pushdown(p);
        while(pl)pushdown(p=pl);
        return p;
    }
    inline void split(Re x,Re y){makeroot(x),access(y),splay(y);}
    inline void link(Re x,Re y){makeroot(x),tr[x].fa=y;}
    inline void cut(Re x,Re y){
        makeroot(x);
        if(findroot(y)==x&&tr[x].fa==y&&!tr[x].ps[1])
            tr[x].fa=tr[y].ps[0]=0,pushup(y);
    }
    inline int lca(Re x,Re y){access(x);return access(y);}
}LCT;
int main(){
    in(n),in(T),in(root),m=n-1;
    while(m--)in(x),in(y),LCT.link(x,y);LCT.makeroot(root);
    while(T--)in(x),in(y),printf("%d\n",LCT.lca(x,y));
}

五:【树分治】

1.【点分治】

【模板】 点分治 \(1\) \(\text{[P3806]}\)

(1).【分各子树计算】

const int N=1e4+3,M=103,INF=1e7+3,inf=2e9;
int n,m,o,x,y,z,T,maxK,K[M],Ans[M],head[N];
struct QAQ{int w,to,next;}a[N<<1];
inline void add(Re x,Re y,Re z){a[++o].w=z,a[o].to=y,a[o].next=head[x],head[x]=o;}
inline void in(Re &x){
    int f=0;x=0;char c=getchar();
    while(c<'0'||c>'9')f|=c=='-',c=getchar();
    while(c>='0'&&c<='9')x=(x<<1)+(x<<3)+(c^48),c=getchar();
    x=f?-x:x;
}
int rt,sum,dd[N],vis[N],tmp[N],dis[N],maxp[N],size[N];bool judge[INF];
inline void getrt(Re x,Re fa){
    size[x]=1,maxp[x]=0;
    for(Re i=head[x],to;i;i=a[i].next)
        if(!vis[to=a[i].to]&&to!=fa)
            getrt(to,x),size[x]+=size[to],maxp[x]=max(maxp[x],size[to]);
    maxp[x]=max(maxp[x],sum-size[x]);
    if(maxp[x]<maxp[rt])rt=x;
}
inline void getdis(Re x,Re fa,Re w){
    if(dis[fa]+w>maxK)return;
    dd[++dd[0]]=dis[x]=dis[fa]+w;
    for(Re i=head[x],to;i;i=a[i].next)
        if(!vis[to=a[i].to]&&to!=fa)getdis(to,x,a[i].w);
}
inline void calc(Re x){
    tmp[0]=0,judge[tmp[++tmp[0]]=0]=1;
    for(Re i=head[x],to;i;i=a[i].next)
        if(!vis[to=a[i].to]){
            dis[x]=dd[0]=0,getdis(to,x,a[i].w);
            for(Re j=1;j<=dd[0];++j)
                for(Re k=1;k<=T;++k)
                    if(K[k]>=dd[j])Ans[k]|=judge[K[k]-dd[j]];
            for(Re j=1;j<=dd[0];++j)judge[tmp[++tmp[0]]=dd[j]]=1;
        }
    for(Re i=1;i<=tmp[0];++i)judge[tmp[i]]=0;
}
inline void sakura(Re x){
    Re now=sum;vis[x]=1,calc(x);//注意一定要开一个局部变量记录当前连通块大小
    for(Re i=head[x],to;i;i=a[i].next)
        if(!vis[to=a[i].to])
            sum=size[to]>size[x]?now-size[x]:size[to],maxp[rt=0]=inf,getrt(to,x),sakura(rt);//注意不能直接取size[to]
}
int main(){
    in(n),in(T),m=n-1;
    while(m--)in(x),in(y),in(z),add(x,y,z),add(y,x,z);
    for(Re i=1;i<=T;++i)in(K[i]),maxK=max(maxK,K[i]);
    sum=n,maxp[rt=0]=inf,getrt(1,0),sakura(rt);
    for(Re i=1;i<=T;++i)puts(Ans[i]?"AYE":"NAY");
}

(2).【容斥计算】

const int N=4e4+3,inf=2e9;
int n,m,o,x,y,z,K,head[N];LL Ans;
struct QAQ{int w,to,next;}a[N<<1];
inline void add(Re x,Re y,Re z){a[++o].w=z,a[o].to=y,a[o].next=head[x],head[x]=o;}
int rt,sum,cnt,tmp[N],dis[N],vis[N],maxp[N],size[N];
inline void getrt(Re x,Re fa){
    size[x]=1,maxp[x]=0;
    for(Re i=head[x],to;i;i=a[i].next)
        if(!vis[to=a[i].to]&&to!=fa)
            getrt(to,x),size[x]+=size[to],maxp[x]=max(maxp[x],size[to]);
    maxp[x]=max(maxp[x],sum-size[x]);
    if(maxp[x]<maxp[rt])rt=x;
}
inline void getdis(Re x,Re fa,Re w){
    if(dis[fa]+w>K)return;
    tmp[++cnt]=dis[x]=dis[fa]+w;
    for(Re i=head[x],to;i;i=a[i].next)
        if(!vis[to=a[i].to]&&to!=fa)getdis(to,x,a[i].w);
}
inline int calc(Re x,Re d){
    Re ans=0;cnt=0,tmp[++cnt]=dis[x]=d;
    for(Re i=head[x],to;i;i=a[i].next)
        if(!vis[to=a[i].to])getdis(to,x,a[i].w);
    sort(tmp+1,tmp+cnt+1);
    Re R=cnt;
    for(Re i=1;i<=cnt&&i<R;++i){
        while(i<R&tmp[i]+tmp[R]>K)--R;
        if(i<R)ans+=(R-(i+1)+1);
    }
    return ans;
}
inline void sakura(Re x){
    Re now=sum;vis[x]=1,Ans+=calc(x,0);
    for(Re i=head[x],to;i;i=a[i].next)
        if(!vis[to=a[i].to])
            Ans-=calc(to,a[i].w),sum=size[to]>size[x]?now-size[x]:size[to],maxp[rt=0]=inf,getrt(to,x),sakura(rt);
}
int main(){
    in(n),m=n-1;
    while(m--)in(x),in(y),in(z),add(x,y,z),add(y,x,z);
    in(K);
    sum=n,maxp[rt=0]=inf,getrt(1,0),sakura(rt);
    printf("%lld\n",Ans);
}

2.【链分治】

3.【点分树(动态点分治)】

【模板】 点分树 / 震波 \(\text{[P6329]}\) \(\text{[Bzoj3730]}\)

(1).【暴跳祖先、在线查询点距离】

const int N=1e5+3,inf=2e9,logN=17;
int n,m,o,x,y,T,op,lastans,A[N],deep[N],head[N];
struct QAQ{int to,next;}a[N<<1];
inline void add(Re x,Re y){a[++o].to=y,a[o].next=head[x],head[x]=o;}
struct LCA{
    int fa[N],top[N],son[N],size[N];
    inline void dfs1(Re x,Re Fa){
        size[x]=1,deep[x]=deep[fa[x]=Fa]+1;
        for(Re i=head[x],to;i;i=a[i].next)
            if((to=a[i].to)!=Fa){
                dfs1(to,x),size[x]+=size[to];
                if(size[son[x]]<size[to])son[x]=to;
            }
    }
    inline void dfs2(Re x,Re rt){
        top[x]=rt;
        if(!son[x])return;
        dfs2(son[x],rt);
        for(Re i=head[x],to;i;i=a[i].next)
            if((to=a[i].to)!=fa[x]&&to!=son[x])dfs2(to,to);
    }
    inline void build(){dfs1(1,0),dfs2(1,1);}
    inline int lca(Re x,Re y){
        while(top[x]!=top[y]){
            if(deep[top[x]]<deep[top[y]])swap(x,y);
            x=fa[top[x]];
        }
        if(deep[x]>deep[y])swap(x,y);
        return x;
    }
}T1;
inline int Dis(Re x,Re y){return deep[x]+deep[y]-(deep[T1.lca(x,y)]<<1);}//查询原树中点x,y的距离
struct BIT{
    int n;vector<int>C;
    inline void build(Re N){C.resize((n=N)+1);}//使用到的上限为n,空间开n+1
    inline void add(Re x,Re v){++x;while(x<=n)C[x]+=v,x+=x&-x;}//由于dis可能为0,所以在BIT里面统计向后移一位,查询同理
    inline int ask(Re x){++x,x=min(x,n);Re ans=0;while(x)ans+=C[x],x-=x&-x;return ans;}//注意要和维护的上界取最小值,防止越界
}TR1[N],TR2[N];
int rt,sum,fa[N],vis[N],maxp[N],size[N];
inline void getrt(Re x,Re fa){//获取该连通块的重心
    size[x]=1,maxp[x]=0;
    for(Re i=head[x],to;i;i=a[i].next)
        if(!vis[to=a[i].to]&&to!=fa)
            getrt(to,x),size[x]+=size[to],maxp[x]=max(maxp[x],size[to]);
    maxp[x]=max(maxp[x],sum-size[x]);
    if(maxp[x]<maxp[rt])rt=x;
}
inline void sakura(Re x,Re Fa){//处理重心x所囊括的连通块
    Re now=sum;vis[x]=1,fa[x]=Fa;
    TR1[x].build(now/2+1),TR2[x].build(now+1);//由重心性质可知,TR1会使用[0,now/2],TR2会使用[1,now],向后移一位变为[1,now/2+1]和[2,now+1]
    for(Re i=head[x],to;i;i=a[i].next)
        if(!vis[to=a[i].to])
            sum=size[to]>size[x]?now-size[x]:size[to],maxp[rt=0]=inf,getrt(to,0),sakura(rt,x);//注意子连通块大小不要直接用size[to]
}
inline void change(Re x,Re v){
    TR1[x].add(0,v);//subtree(x)
    for(Re i=x;fa[i];i=fa[i]){//在虚树上面跳父亲 
        Re tmp=Dis(x,fa[i]);
        TR1[fa[i]].add(tmp,v);
        TR2[i].add(tmp,v);
    }
}
inline int ask(Re x,Re K){
    Re ans=TR1[x].ask(K);//subtree(x)
    for(Re i=x;fa[i];i=fa[i]){//在虚树上面跳父亲 
        Re tmp=Dis(x,fa[i]);if(tmp>K)continue;
        ans+=TR1[fa[i]].ask(K-tmp);
        ans-=TR2[i].ask(K-tmp);
    }
    return ans;
}
int main(){
    in(n),in(T),m=n-1;
    for(Re i=1;i<=n;++i)in(A[i]);
    while(m--)in(x),in(y),add(x,y),add(y,x);
    T1.build(),sum=n,maxp[rt=0]=inf,getrt(1,0),sakura(rt,0);
    for(Re i=1;i<=n;++i)change(i,A[i]);
    while(T--){
        in(op),in(x),in(y),x^=lastans,y^=lastans;
        if(op)change(x,y-A[x]),A[x]=y;
        else printf("%d\n",lastans=ask(x,y));
    }
}

(2).【预处理祖先及距离】

const int N=1e5+3,inf=2e9,logN=17;
int n,m,o,x,y,T,op,lastans,A[N],head[N];
struct QAQ{int to,next;}a[N<<1];
inline void add(Re x,Re y){a[++o].to=y,a[o].next=head[x],head[x]=o;}
struct BIT{
    int n;vector<int>C;
    inline void build(Re N){C.resize((n=N)+1);}//使用到的上限为n,空间开n+1
    inline void add(Re x,Re v){++x;while(x<=n)C[x]+=v,x+=x&-x;}//由于dis可能为0,所以在BIT里面统计向后移一位,查询同理
    inline int ask(Re x){++x,x=min(x,n);Re ans=0;while(x)ans+=C[x],x-=x&-x;return ans;}//注意要和维护的上界取最小值,防止越界
}TR1[N],TR2[N];
int rt,sum,gs[N],vis[N],maxp[N],size[N],frt[N][20],fdis[N][20];
inline void getrt(Re x,Re fa){//获取该连通块的重心
    size[x]=1,maxp[x]=0;
    for(Re i=head[x],to;i;i=a[i].next)
        if(!vis[to=a[i].to]&&to!=fa)
            getrt(to,x),size[x]+=size[to],maxp[x]=max(maxp[x],size[to]);
    maxp[x]=max(maxp[x],sum-size[x]);
    if(maxp[x]<maxp[rt])rt=x;
}
inline void getdis(Re x,Re rt,Re fa,Re d){//遍历该连通块预处理dis
    frt[x][++gs[x]]=rt,fdis[x][gs[x]]=d;//顺手把祖先也存下来,后面一起访问
    for(Re i=head[x],to;i;i=a[i].next)
        if(!vis[to=a[i].to]&&to!=fa)getdis(to,rt,x,d+1);
}
inline void sakura(Re x){//处理重心x所囊括的连通块
    Re now=sum;vis[x]=1,getdis(x,x,0,0);
    TR1[x].build(now/2+1),TR2[x].build(now+1);//由重心性质可知,TR1会使用[0,now/2],TR2会使用[1,now],向后移一位变为[1,now/2+1]和[2,now+1]
    for(Re i=head[x],to;i;i=a[i].next)
        if(!vis[to=a[i].to])
            sum=size[to]>size[x]?now-size[x]:size[to],maxp[rt=0]=inf,getrt(to,0),sakura(rt);//注意子连通块大小不要直接用size[to]
}
inline void change(Re x,Re v){
    TR1[x].add(0,v);//subtree(x)
    for(Re i=gs[x];i>=2;--i){//注意要倒序枚举
        Re tmp=fdis[x][i-1];
        TR1[frt[x][i-1]].add(tmp,v);
        TR2[frt[x][i]].add(tmp,v);
    }
}
inline int ask(Re x,Re K){
    Re ans=TR1[x].ask(K);//subtree(x)
    for(Re i=gs[x];i>=2;--i){//注意要倒序枚举
        Re tmp=fdis[x][i-1];if(tmp>K)continue;
        ans+=TR1[frt[x][i-1]].ask(K-tmp);
        ans-=TR2[frt[x][i]].ask(K-tmp);
    }
    return ans;
}
int main(){
    in(n),in(T),m=n-1;
    for(Re i=1;i<=n;++i)in(A[i]);
    while(m--)in(x),in(y),add(x,y),add(y,x);
    sum=n,maxp[rt=0]=inf,getrt(1,0),sakura(rt);
    for(Re i=1;i<=n;++i)change(i,A[i]);
    while(T--){
        in(op),in(x),in(y),x^=lastans,y^=lastans;
        if(op)change(x,y-A[x]),A[x]=y;
        else printf("%d\n",lastans=ask(x,y));
    }
}

六:【基环树】

1.【基环树森林求直径】

【模板】 \(\text{Island}\) \(\text{[P4381]}\)

#pragma comment(linker, "/STACK:102400000,102400000")
const int N=1e6+3;
#define LL long long
int n,m,x,y,o=1,h,t,st,cnt,In[N],cir[N],pan[N],head[N];LL ans,tmp,Q[N<<1],S[N<<1],dp[N<<1],dis[N];
struct QAQ{int w,to,next;}a[N<<1];
inline void add(Re x,Re y,Re z){a[++o].w=z,a[o].to=y,a[o].next=head[x],head[x]=o;}
inline LL max(LL a,LL b){return a>b?a:b;}
inline int find_circle(Re x,Re p){
    if(In[x]){st=x,cir[++cnt]=x,pan[x]=1;return 1;}
    In[x]=1;
    for(Re i=head[x];i;i=a[i].next)
        if(i!=(p^1)&&find_circle(a[i].to,i)){
            pan[x]=1;
            if(x==st){S[0]=S[1]-a[i].w;return 0;}
            cir[++cnt]=x,S[cnt]=S[cnt-1]+a[i].w;
            return 1;
        }
    return 0;
}
inline void Tree_dp(Re x){
    pan[x]=1;
    for(Re i=head[x],to;i;i=a[i].next)
        if(!pan[to=a[i].to]){
            Tree_dp(to);
            tmp=max(tmp,dis[x]+a[i].w+dis[to]);
            dis[x]=max(dis[x],dis[to]+a[i].w);
        }
}
inline LL pp(Re j){return dp[j]-S[j];}
inline LL sakura(Re root){
    S[0]=cnt=st=0;find_circle(root,0);
    LL ans1=0,ans2=0;tmp=0;
    for(Re i=1;i<=cnt;++i){
        tmp=0,Tree_dp(cir[i]),ans1=max(ans1,tmp);
        dp[i+cnt]=dp[i]=dis[cir[i]];
        S[i+cnt]=S[i+cnt-1]+S[i]-S[i-1];
    }
    h=1,t=0,Q[++t]=1;//ans=max(dp[i]+dp[j]+S[i]-S[j]) i-j+1<=cnt i-cnt+1<=j<i
    for(Re i=2;i<=(cnt<<1);++i){
        while(h<=t&&Q[h]<i-cnt+1)++h;
        if(h<=t)ans2=max(ans2,dp[i]+S[i]+pp(Q[h]));
        while(h<=t&&pp(Q[t])<=pp(i))--t;
        Q[++t]=i;
    }
    return max(ans1,ans2);
}
int main(){
    in(n);
    for(Re i=1;i<=n;++i)in(x),in(y),add(i,x,y),add(x,i,y);
    for(Re i=1;i<=n;++i)if(!pan[i])ans+=sakura(i);
    printf("%lld",ans);
}

七:【虚树】

【模板】 \(\text{Kingdom and its Cities [CF613D]}\)

const int N=1e5+3,logN=17;
int n,m,x,y,o,T,dfn_o,dfn[N],deep[N],head[N];
struct QAQ{int to,next;}a[N<<1];
inline void add(Re x,Re y){a[++o].to=y,a[o].next=head[x],head[x]=o;}
struct LCA{
    int ant[N][20];
    inline void dfs(Re x,Re fa){
        deep[x]=deep[ant[x][0]=fa]+1,dfn[x]=++dfn_o;
        for(Re j=1;(1<<j)<=deep[x];++j)ant[x][j]=ant[ant[x][j-1]][j-1];
        for(Re i=head[x];i;i=a[i].next)if(a[i].to!=fa)dfs(a[i].to,x);
    }
    inline int lca(Re x,Re y){
        if(deep[x]<deep[y])swap(x,y);
        for(Re j=logN;j>=0;--j)if(deep[ant[x][j]]>=deep[y])x=ant[x][j];
        if(x==y)return x;
        for(Re j=logN;j>=0;--j)if(ant[x][j]!=ant[y][j])x=ant[x][j],y=ant[y][j];
        return ant[x][0];
    }
}T1;
inline bool cmp(Re x,Re y){return dfn[x]<dfn[y];}
struct Virtual_Tree{
    int t,o,ans,A[N],Q[N],key[N],head[N];
    struct QAQ{int to,next;}a[N<<1];
    inline void add(Re x,Re y){a[++o].to=y,a[o].next=head[x],head[x]=o;}
    inline void insert(Re x){//虚树构造
        Re lca=T1.lca(Q[t],x);
        while(t>1&&deep[Q[t-1]]>deep[lca])add(Q[t-1],Q[t]),--t;
        if(t&&deep[Q[t]]>deep[lca])add(lca,Q[t--]);
        if(!t||Q[t]!=lca)Q[++t]=lca;
        Q[++t]=x;
    }
    inline void dfs(Re x){//遍历虚树
        if(key[x])//如果x是关键点
            for(Re i=head[x],to;i;i=a[i].next){
                dfs(to=a[i].to);
                if(key[to])key[to]=0,++ans;
            }
        else{//如果x不是关键点
            for(Re i=head[x],to;i;i=a[i].next)
                dfs(to=a[i].to),key[x]+=key[to],key[to]=0;
            if(key[x]>1)key[x]=0,++ans;
        }
        head[x]=0;
    }
    inline void sakura(){
        in(m);Re flag=1;ans=o=t=0;
        for(Re i=1;i<=m;++i)in(A[i]),key[A[i]]=1;
        for(Re i=1;i<=m&&flag;++i)flag&=(!key[T1.ant[A[i]][0]]);
        if(!flag){
            for(Re i=1;i<=m;++i)key[A[i]]=0;
            puts("-1");return;
        }
        sort(A+1,A+m+1,cmp),Q[++t]=1;
        for(Re i=1+(A[1]==1);i<=m;++i)insert(A[i]);
        while(t>1)add(Q[t-1],Q[t]),--t;
        dfs(1),key[1]=0;//根节点可能没有还清除
        printf("%d\n",ans);
    }
}T2;
int main(){
    in(n),m=n-1;
    while(m--)in(x),in(y),add(x,y),add(y,x);
    T1.dfs(1,0),in(T);
    while(T--)T2.sakura();
}

八:【树上启发式合并(Dsu on tree)】

【模板】 \(\text{Lomsat gelral [CF600E]}\)

const int N=1e5+3;
int o,n,m,x,y,t,K,tmp,A[N],Q[N],cnt[N],son[N],size[N],head[N];LL ans,Ans[N];
struct QAQ{int to,next;}a[N<<1];
inline void add(Re x,Re y){a[++o].to=y,a[o].next=head[x],head[x]=o;}
inline void dfs(Re x,Re fa){//预处理重儿子
    size[x]=1;
    for(Re i=head[x],to;i;i=a[i].next)
        if((to=a[i].to)!=fa){
            dfs(to,x),size[x]+=size[to];
            if(size[to]>size[son[x]])son[x]=to;
        }
}
inline void CL(){//清空贡献,从zero开始
    while(t)cnt[Q[t--]]=0;ans=tmp=0;
}
inline void insert(Re x){//加入点x的贡献
    ++cnt[Q[++t]=A[x]];
    if(cnt[A[x]]>tmp)tmp=cnt[ans=A[x]];
    else if(cnt[A[x]]==tmp)ans+=A[x];
}
inline void addson(Re x,Re fa){//加入subtree(x)的贡献(以x为根的整棵子树)
    insert(x);
    for(Re i=head[x],to;i;i=a[i].next)
        if((to=a[i].to)!=fa)addson(to,x);
}
inline void sakura(Re x,Re fa){
    for(Re i=head[x],to;i;i=a[i].next)
        if((to=a[i].to)!=fa&&to!=son[x])sakura(to,x),CL();//计算轻儿子的答案并清空贡献
    if(son[x])sakura(son[x],x);//计算重儿子的答案并保留subtree(son[x])(以son[x]为根的整棵子树贡献)
    for(Re i=head[x],to;i;i=a[i].next)
        if((to=a[i].to)!=fa&&to!=son[x])addson(to,x);//加入subtree(x)-subtree(son[x])-x(以x的所有轻儿子为根的子树贡献)
    insert(x),Ans[x]=ans;//注意还要把x的贡献也加进去
}
int main(){
    in(n),m=n-1;
    for(Re i=1;i<=n;++i)in(A[i]);
    while(m--)in(x),in(y),add(x,y),add(y,x);
    dfs(1,0),sakura(1,0);
    for(Re i=1;i<=n;++i)printf("%lld ",Ans[i]);
}
posted @ 2019-11-12 08:17  辰星凌  阅读(872)  评论(0编辑  收藏  举报