树链剖分习题 1(基础)

树链剖分习题

      • 基础知识
      • P3384 【模板】轻重链剖分 (树剖入门题)
      • P2590 [ZJOI2008]树的统计 (树剖入门题)
      • P2146 [NOI2015]软件包管理器 (树剖入门题)
      • P2486 [SDOI2011]染色 (线段树入门题)
      • P3979 遥远的国度 (树剖换根)

基础知识

轻重链剖分主要运用线段树维护路径上点权的修改

  • dfs1:需要处理出每个节点的 fa,每个点子树的大小 num ,并根据 num 得到每个节点的重儿子 son,处理出深度 depth
  • dfs2:需要处理出每个节点的链顶 top,处理出每个点 DFS序 start
  • 子树查询:查询根为 u 的子树: s t a r t [ u ] start[u] start[u] s t a r t [ u ] + n u m [ u ] − 1 start[u]+num[u]-1 start[u]+num[u]1 这一段即可
  • 路径查询:查询 u 和 v 这一段路径上的点:当 u 和 v 不在一条重链上时(即 t o p [ u ] ! = t o p [ v ] top[u]!=top[v] top[u]!=top[v]),让链顶深度较深的往上跳(假设 top[u]的深度深),那么就可以 累加 s t a r t [ t o p [ u ] ] start[top[u]] start[top[u]] s t a r t [ u ] start[u] start[u] 这一段,同时 u 往上跳变成链顶( t o p [ u ] top[u] top[u])的父亲(即 u = f a [ t o p [ u ] ] u=fa[top[u]] u=fa[top[u]]),一直循环。直到 u 和 v 在同一条链上,再累加一次即可

P3384 【模板】轻重链剖分 (树剖入门题)

树链剖分习题 1(基础)_第1张图片
链接:https://www.luogu.com.cn/problem/P3384

思路:线段树维护点权和即可,支持区间修改和查询

#include <bits/stdc++.h>
#define ll long long
#define ls (rt<<1)
#define rs (rt<<1|1)
using namespace std;
const int maxn=1e5+10;

int n,m,r,mod;
vector<int> e[maxn];
int w[maxn],depth[maxn],fa[maxn],son[maxn],num[maxn];
int start[maxn],times,id[maxn],top[maxn];

int head[maxn],cnt;
struct Edge
{
    int to,nxt;
}edges[maxn<<1];
void add(int u,int v)
{
    edges[++cnt].to=v;
    edges[cnt].nxt=head[u];
    head[u]=cnt;
}

ll st[maxn<<2],lazy[maxn<<2];
void pushUp(int rt)
{
    st[rt]=(st[ls]+st[rs])%mod;
}
void pushDown(int rt,int L,int R)
{
    if(lazy[rt])
    {
        int mid=(L+R)>>1;
        st[ls]=(st[ls]+lazy[rt]*(mid-L+1))%mod;
        st[rs]=(st[rs]+lazy[rt]*(R-mid))%mod;
        lazy[ls]+=lazy[rt];
        lazy[rs]+=lazy[rt];
        lazy[rt]=0;
    }
}
void build(int rt,int l,int r)
{
	lazy[rt]=0;
    if(l==r)
    {
        st[rt]=w[id[l]]%mod;
        return;
    }
    int mid=(l+r)>>1;
    build(ls,l,mid);
    build(rs,mid+1,r);
    pushUp(rt);
}
int query(int rt,int l,int r,int L,int R)
{
    if(l<=L&&R<=r)
        return st[rt];
    pushDown(rt,L,R);
    int mid=(L+R)>>1;
    int ans=0;
    if(l<=mid) ans+=query(ls,l,r,L,mid);
    if(r>mid) ans+=query(rs,l,r,mid+1,R);
    return ans%mod;
}
void update(int rt,int l,int r,int L,int R,int val)
{
    if(l<=L&&R<=r)
    {
        st[rt]+=(R-L+1)*val;
        lazy[rt]+=val;
        return;
    }
    pushDown(rt,L,R);
    int mid=(L+R)>>1;
    if(l<=mid) update(ls,l,r,L,mid,val);
    if(r>mid) update(rs,l,r,mid+1,R,val);
    pushUp(rt);
}

void dfs1(int u)
{
    num[u]=1;
    for(int i=head[u];i!=-1;i=edges[i].nxt)
    {
        int v=edges[i].to;
        if(v==fa[u]) continue;
        depth[v]=depth[u]+1;
        fa[v]=u;
        dfs1(v);
        num[u]+=num[v];
        if(num[v]>num[son[u]]) son[u]=v;
    }
}
void dfs2(int u,int x)
{
    start[u]=++times;
    id[times]=u;
    top[u]=x;
    if(!son[u]) return;
    dfs2(son[u],x);
    for(int i=head[u];i!=-1;i=edges[i].nxt)
    {
        int v=edges[i].to;
        if(v==fa[u]||v==son[u]) continue;
        dfs2(v,v);
    }
}
int qTree(int u)
{
    return query(1,start[u],start[u]+num[u]-1,1,n);
}
void updTree(int u,int val)
{
    update(1,start[u],start[u]+num[u]-1,1,n,val);
}
int qPath(int u,int v)
{
    int ans=0;
    while(top[u]!=top[v])
    {
        if(depth[top[u]]<depth[top[v]]) swap(u,v);
        ans+=query(1,start[top[u]],start[u],1,n);
        ans%=mod;
        u=fa[top[u]];
    }
    if(depth[u]>depth[v]) swap(u,v);
    ans+=query(1,start[u],start[v],1,n);
    return ans%mod;
}
void updPath(int u,int v,int val)
{
    val%=mod;
    while(top[u]!=top[v])
    {
        if(depth[top[u]]<depth[top[v]]) swap(u,v);
        update(1,start[top[u]],start[u],1,n,val);
        u=fa[top[u]];
    }
    if(depth[u]>depth[v]) swap(u,v);
    update(1,start[u],start[v],1,n,val);
}

int main()
{
    memset(head,-1,sizeof(head)),cnt=0;
    scanf("%d%d%d%d",&n,&m,&r,&mod);
    for(int i=1;i<=n;++i) scanf("%d",&w[i]);
    for(int i=1;i<=n-1;++i)
    {
        int u,v;
        scanf("%d%d",&u,&v);
        add(u,v),add(v,u);
    }
    dfs1(r);
    dfs2(r,r);
	build(1,1,n);
    int op,u,v,dx;
    while(m--)
    {
        scanf("%d",&op);
        if(op==1)
        {
            scanf("%d%d%d",&u,&v,&dx);
            updPath(u,v,dx);
        }
        else if(op==2)
        {
            scanf("%d%d",&u,&v);
            printf("%d\n",qPath(u,v));
        }
        else if(op==3)
        {
            scanf("%d%d",&u,&dx);
            updTree(u,dx);
        }
        else
        {
            scanf("%d",&u);
            printf("%d\n",qTree(u));
        }
    }
    return 0;
}

P2590 [ZJOI2008]树的统计 (树剖入门题)

树链剖分习题 1(基础)_第2张图片
链接:https://www.luogu.com.cn/problem/P2590
思路:线段树维护单点修改、区间最大值查询、区间和查询

#include <bits/stdc++.h>
#define ll long long
#define ls (rt<<1)
#define rs (rt<<1|1)
using namespace std;
const int maxn=3e4+10,inf=2e9;

int n,m;
int sum[maxn<<2],mx[maxn<<2],w[maxn];
vector<int> e[maxn];
int depth[maxn],fa[maxn],son[maxn],num[maxn];
int start[maxn],id[maxn],top[maxn],times;
void dfs1(int u)
{
    num[u]=1;
    for(auto v: e[u])
    {
        if(v==fa[u]) continue;
        fa[v]=u;
        depth[v]=depth[u]+1;
        dfs1(v);
        num[u]+=num[v];
        if(num[v]>num[son[u]]) son[u]=v;
    }
}
void dfs2(int u,int x)
{
    start[u]=++times;
    id[times]=u;
    top[u]=x;
    if(!son[u]) return;
    dfs2(son[u],x);
    for(auto v: e[u])
    {
        if(v==fa[u]||v==son[u]) continue;
        dfs2(v,v);
    }
}
void pushUp(int rt)
{
    sum[rt]=sum[ls]+sum[rs];
    mx[rt]=max(mx[ls],mx[rs]);
}
void build(int rt,int L,int R)
{
    if(L==R)
    {
        sum[rt]=w[id[L]];
        mx[rt]=w[id[L]];
        return;
    }
    int mid=(L+R)>>1;
    build(ls,L,mid);
    build(rs,mid+1,R);
    pushUp(rt);
}
void update(int rt,int p,int L,int R,int val)
{
    if(L==R)
    {
        sum[rt]=val;
        mx[rt]=val;
        return;
    }
    int mid=(L+R)>>1;
    if(p<=mid) update(ls,p,L,mid,val);
    if(p>mid) update(rs,p,mid+1,R,val);
    pushUp(rt);
}
int queryMax(int rt,int l,int r,int L,int R)
{
    if(l<=L&&R<=r)
        return mx[rt];
    int ans=-inf;
    int mid=(L+R)>>1;
    if(l<=mid) ans=max(ans,queryMax(ls,l,r,L,mid));
    if(r>mid) ans=max(ans,queryMax(rs,l,r,mid+1,R));
    return ans;
}
int querySum(int rt,int l,int r,int L,int R)
{
    if(l<=L&&R<=r)
        return sum[rt];
    int ans=0;
    int mid=(L+R)>>1;
    if(l<=mid) ans+=querySum(ls,l,r,L,mid);
    if(r>mid) ans+=querySum(rs,l,r,mid+1,R);
    return ans;
}

int qPathMax(int u,int v)
{
    int ans=-inf;
    while(top[u]!=top[v])
    {
        if(depth[top[u]]<depth[top[v]]) swap(u,v);
        ans=max(ans,queryMax(1,start[top[u]],start[u],1,n));
        u=fa[top[u]];
    }
    if(depth[u]>depth[v]) swap(u,v);
    ans=max(ans,queryMax(1,start[u],start[v],1,n));
    return ans;
}
int qPathSum(int u,int v)
{
    int ans=0;
    while(top[u]!=top[v])
    {
        if(depth[top[u]]<depth[top[v]]) swap(u,v);
        ans+=querySum(1,start[top[u]],start[u],1,n);
        u=fa[top[u]];
    }
    if(depth[u]>depth[v]) swap(u,v);
    ans+=querySum(1,start[u],start[v],1,n);
    return ans;
}

int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n-1;++i)
    {
        int u,v;
        scanf("%d%d",&u,&v);
        e[u].push_back(v);
        e[v].push_back(u);
    }
    for(int i=1;i<=n;++i) scanf("%d",&w[i]);
    dfs1(1);
    dfs2(1,1);
    build(1,1,n);
    scanf("%d",&m);
    string op;
    int u,v,t;
    while(m--)
    {
        cin>>op;
        if(op=="QMAX")
        {
            scanf("%d%d",&u,&v);
            printf("%d\n",qPathMax(u,v));
        }
        else if(op=="QSUM")
        {
            scanf("%d%d",&u,&v);
            printf("%d\n",qPathSum(u,v));
        }
        else
        {
            scanf("%d%d",&u,&t);
            update(1,start[u],1,n,t);
        }
    }
    return 0;
}

P2146 [NOI2015]软件包管理器 (树剖入门题)

树链剖分习题 1(基础)_第3张图片
坑点:线段树写岔了。一开始 lazy 设置成 0 不会往下更新。

#include <bits/stdc++.h>
#define ls (rt<<1)
#define rs (rt<<1|1)
using namespace std;
const int maxn=1e5+10;
int n,m;
vector<int> e[maxn];
int depth[maxn],fa[maxn],son[maxn],num[maxn];
int start[maxn],top[maxn],id[maxn],times;
int st[maxn<<2],lazy[maxn<<2];

void dfs1(int u)
{
    num[u]=1;
    for(auto v: e[u])
    {
        if(v==fa[u]) continue;
        depth[v]=depth[u]+1;
        fa[v]=u;
        dfs1(v);
        num[u]+=num[v];
        if(num[v]>num[son[u]]) son[u]=v;
    }
}
void dfs2(int u,int x)
{
    start[u]=++times;
    id[times]=u;
    top[u]=x;
    if(!son[u]) return;
    dfs2(son[u],x);
    for(auto v: e[u])
    {
        if(v==fa[u]||v==son[u]) continue;
        dfs2(v,v);
    }
}
void pushUp(int rt)
{
    st[rt]=st[ls]+st[rs];
}
void pushDown(int rt,int L,int R)
{
    if(lazy[rt]!=-1)
    {
        int mid=(L+R)>>1;
        st[ls]=(mid-L+1)*lazy[rt];
        st[rs]=(R-mid)*lazy[rt];
        lazy[ls]=lazy[rt];
        lazy[rs]=lazy[rt];
        lazy[rt]=-1;
    }
}

int query(int rt,int l,int r,int L,int R)
{
    if(l<=L&&R<=r) return st[rt];
    pushDown(rt,L,R);
    int mid=(L+R)>>1;
    int ans=0;
    if(l<=mid) ans+=query(ls,l,r,L,mid);
    if(r>mid) ans+=query(rs,l,r,mid+1,R);
    return ans;
}
void update(int rt,int l,int r,int L,int R,int val)
{
    if(l<=L&&R<=r)
    {
        st[rt]=(R-L+1)*val;
        lazy[rt]=val;
        return;
    }
    pushDown(rt,L,R);
    int mid=(L+R)>>1;
    if(l<=mid) update(ls,l,r,L,mid,val);
    if(r>mid) update(rs,l,r,mid+1,R,val);
    pushUp(rt);
}
void updPath(int u,int v,int dx)
{
    while(top[u]!=top[v])
    {
        if(depth[top[u]]<depth[top[v]]) swap(u,v);
        update(1,start[top[u]],start[u],1,n,dx);
        u=fa[top[u]];
    }
    if(depth[u]>depth[v]) swap(u,v);
    update(1,start[u],start[v],1,n,dx);
}
int qPathIn(int u,int v)
{
    int len=0,ans=0;
    while(top[u]!=top[v])
    {
        if(depth[top[u]]<depth[top[v]]) swap(u,v);
        len+=start[u]-start[top[u]]+1;
        ans+=query(1,start[top[u]],start[u],1,n);
        u=fa[top[u]];
    }
    if(depth[u]>depth[v]) swap(u,v);
    len+=start[v]-start[u]+1;
    ans+=query(1,start[u],start[v],1,n);
    return len-ans;
}

int main()
{
    memset(lazy,-1,sizeof(lazy));
    scanf("%d",&n);
    for(int i=2;i<=n;++i)
    {
        int u;
        scanf("%d",&u);
        u++;
        e[u].push_back(i);
        e[i].push_back(u);
    }
    dfs1(1);
    dfs2(1,1);
    scanf("%d",&m);
    string op;
    int u;
    while(m--)
    {
        cin>>op;
        scanf("%d",&u);
        u++;
        if(op=="install")
        {
            printf("%d\n",qPathIn(1,u));
            updPath(1,u,1);
        }
        else
        {
            printf("%d\n",query(1,start[u],start[u]+num[u]-1,1,n));
            update(1,start[u],start[u]+num[u]-1,1,n,0);
        }
    }
    return 0;
}

P2486 [SDOI2011]染色 (线段树入门题)

树链剖分习题 1(基础)_第4张图片
题意:维护线段树支持树上单点颜色的修改,以及颜色段数的询问
思路:询问路径上点的不同颜色段数,需要维护路径左右两端的上一个颜色,last1和last2,当 u 往上走时 更新last1,并且查询query1需要从后往前查询。当 v 往上走时,更新last2,查询query2也是从后往前。当 u 和 v 在同一条链上时,继续判断谁在上,u在上,那么就调用 query2,让 last2 从后往前更新。v 在上同理。最后判断一下last1和last2的颜色,相同需要减 1 。

#include <bits/stdc++.h>
#define ls (rt<<1)
#define rs (rt<<1|1)
using namespace std;
const int maxn=1e5+10;
int n,m;
vector<int> e[maxn];
int depth[maxn],fa[maxn],son[maxn],num[maxn];
int start[maxn],top[maxn],id[maxn],times;
int st[maxn<<2],lazy[maxn<<2],w[maxn];

void dfs1(int u)
{
    num[u]=1;
    for(auto v: e[u])
    {
        if(v==fa[u]) continue;
        depth[v]=depth[u]+1;
        fa[v]=u;
        dfs1(v);
        num[u]+=num[v];
        if(num[v]>num[son[u]]) son[u]=v;
    }
}
void dfs2(int u,int x)
{
    start[u]=++times;
    id[times]=u;
    top[u]=x;
    if(!son[u]) return;
    dfs2(son[u],x);
    for(auto v: e[u])
    {
        if(v==fa[u]||v==son[u]) continue;
        dfs2(v,v);
    }
}
void pushDown(int rt)
{
    if(st[rt]!=-1)
    {
        st[ls]=st[rt];
        st[rs]=st[rt];
        st[rt]=-1;
    }
}
void build(int rt,int L,int R)
{
    if(L==R)
    {
        st[rt]=w[id[L]];
        return;
    }
    int mid=(L+R)>>1;
    build(ls,L,mid);
    build(rs,mid+1,R);
}
int res,last1,last2;
void query1(int rt,int l,int r,int L,int R)
{
    if(st[rt]!=-1)
    {
        if(last1!=st[rt])
            res++;
        last1=st[rt];
        return;
    }
    if(L==R)
    {
        last1=st[rt];
        return;
    }
    pushDown(rt);
    int mid=(L+R)>>1;
    if(r>mid) query1(rs,l,r,mid+1,R);
    if(l<=mid) query1(ls,l,r,L,mid);
}
void query2(int rt,int l,int r,int L,int R)
{
    if(st[rt]!=-1)
    {
        if(last2!=st[rt])
            res++;
        last2=st[rt];
        return;
    }
    if(L==R)
    {
        last2=st[rt];
        return;
    }
    pushDown(rt);
    int mid=(L+R)>>1;
    if(r>mid) query2(rs,l,r,mid+1,R);
    if(l<=mid) query2(ls,l,r,L,mid);
}
void update(int rt,int l,int r,int L,int R,int val)
{
    if(l<=L&&R<=r)
    {
        st[rt]=val;
        return;
    }
    pushDown(rt);
    int mid=(L+R)>>1;
    if(l<=mid) update(ls,l,r,L,mid,val);
    if(r>mid) update(rs,l,r,mid+1,R,val);
}
void updPath(int u,int v,int dx)
{
    while(top[u]!=top[v])
    {
        if(depth[top[u]]<depth[top[v]]) swap(u,v);
        update(1,start[top[u]],start[u],1,n,dx);
        u=fa[top[u]];
    }
    if(depth[u]>depth[v]) swap(u,v);
    update(1,start[u],start[v],1,n,dx);
}
int qPath(int u,int v)
{
    int ans=0;
    last1=last2=-1;
    while(top[u]!=top[v])
    {
        if(depth[top[u]]>depth[top[v]])
        {
            res=0;
            query1(1,start[top[u]],start[u],1,n);
            ans+=res;
            u=fa[top[u]];
        }
        else
        {
            res=0;
            query2(1,start[top[v]],start[v],1,n);
            ans+=res;
            v=fa[top[v]];
        }
    }
    if(depth[u]>depth[v])
    {
        res=0;
        query1(1,start[v],start[u],1,n);
        ans+=res;
        if(last2==last1) ans--;
    }
    else
    {
        res=0;
        query2(1,start[u],start[v],1,n);
        ans+=res;
        if(last2==last1) ans--;
    }
    return ans;
}

int main()
{
    memset(st,-1,sizeof(st));
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;++i) scanf("%d",&w[i]);
    for(int i=1;i<=n-1;++i)
    {
        int u,v;
        scanf("%d%d",&u,&v);
        e[u].push_back(v);
        e[v].push_back(u);
    }
    dfs1(1);
    dfs2(1,1);
    build(1,1,n);
    scanf("%d",&m);
    char op[10];
    int a,b,c;
    while(m--)
    {
        scanf("%s",op);
        if(op[0]=='Q')
        {
            scanf("%d%d",&a,&b);
            printf("%d\n",qPath(a,b));
        }
        else
        {
            scanf("%d%d%d",&a,&b,&c);
            updPath(a,b,c);
        }
    }
    return 0;
}

P3979 遥远的国度 (树剖换根)

树链剖分习题 1(基础)_第5张图片
题意:每次根变了之后,查询的范围也不一样
思路:操作2是路径操作与根无关。操作3每次都与现在根的位置有关。

  • 一共三种情况: l c a ( u , r o o t ) = u , l c a ( u , r o o t ) = r o o t , l c a ( u , r o o t ) = z lca(u,root)=u,lca(u,root)=root,lca(u,root)=z lca(u,root)=u,lca(u,root)=root,lca(u,root)=z,然后特判一下 u ==root的情况
  • l c a ( u , r o o t ) = r o o t lca(u,root)=root lca(u,root)=root,u 是 root 的子树直接查询 u 这棵树即可
  • l c a ( u , r o o t ) = z lca(u,root)=z lca(u,root)=z,u 和 root 在不同的树上,也是直接查询 u 这颗树即可
  • u = = r o o t u == root u==root ,需要查询整棵树
  • l c a ( u , r o o t ) = u lca(u,root)=u lca(u,root)=u,这种情况就相当于查询一个补集,找到一个 v 是 u 的儿子,root 的祖先,然后查询 [ 1 , s t a r t [ v ] − 1 ] [1,start[v]-1] [1start[v]1] [ s t a r t [ v ] + n u m [ v ] , n ] [start[v]+num[v],n] [start[v]+num[v],n] 这两个区间即可
#include <bits/stdc++.h>
#define ll long long
#define ls (rt<<1)
#define rs (rt<<1|1)
using namespace std;
const int maxn=1e5+10;

int n,m,capital;
vector<int> e[maxn];
int fa[maxn],son[maxn],depth[maxn],num[maxn];
int start[maxn],id[maxn],top[maxn],times;
int st[maxn<<2],w[maxn],lazy[maxn<<2];
void dfs1(int u)
{
    num[u]=1;
    for(auto v: e[u])
    {
        if(v==fa[u]) continue;
        depth[v]=depth[u]+1;
        fa[v]=u;
        dfs1(v);
        num[u]+=num[v];
        if(num[v]>num[son[u]]) son[u]=v;
    }
}
void dfs2(int u,int x)
{
    start[u]=++times;
    id[times]=u;
    top[u]=x;
    if(!son[u]) return;
    dfs2(son[u],x);
    for(auto v: e[u])
    {
        if(v==fa[u]||v==son[u]) continue;
        dfs2(v,v);
    }
}
void pushUp(int rt)
{
    st[rt]=min(st[ls],st[rs]);
}
void pushDown(int rt)
{
    if(lazy[rt])
    {
        st[ls]=lazy[rt];
        st[rs]=lazy[rt];
        lazy[ls]=lazy[rt];
        lazy[rs]=lazy[rt];
        lazy[rt]=0;
    }
}
void build(int rt,int L,int R)
{
    if(L==R)
    {
        st[rt]=w[id[L]];
        return;
    }
    int mid=(L+R)>>1;
    build(ls,L,mid);
    build(rs,mid+1,R);
    pushUp(rt);
}
void update(int rt,int l,int r,int L,int R,int val)
{
    if(l<=L&&R<=r)
    {
        st[rt]=val;
        lazy[rt]=val;
        return;
    }
    pushDown(rt);
    int mid=(L+R)>>1;
    if(l<=mid) update(ls,l,r,L,mid,val);
    if(r>mid) update(rs,l,r,mid+1,R,val);
    pushUp(rt);
}
ll query(int rt,int l,int r,int L,int R)
{
    if(l<=L&&R<=r)
        return st[rt];
    pushDown(rt);
    int mid=(L+R)>>1;
    ll ans=(1ll<<31);
    if(l<=mid) ans=min(ans,query(ls,l,r,L,mid));
    if(r>mid) ans=min(ans,query(rs,l,r,mid+1,R));
    return ans;
}
void updPath(int u,int v,int dx)
{
    while(top[u]!=top[v])
    {
        if(depth[top[u]]<depth[top[v]])swap(u,v);
        update(1,start[top[u]],start[u],1,n,dx);
        u=fa[top[u]];
    }
    if(depth[u]>depth[v]) swap(u,v);
    update(1,start[u],start[v],1,n,dx);
}
int lca(int u,int v)
{
    while(top[u]!=top[v])
    {
        if(depth[top[u]]<depth[top[v]]) swap(u,v);
        u=fa[top[u]];
    }
    if(depth[u]>depth[v]) swap(u,v);
    return u;
}
int qTree(int u)
{
    if(u==capital) return query(1,start[1],start[1]+num[1]-1,1,n);
    else if(lca(u,capital)==u)
    {
        int x=capital;
        for(auto v: e[u])
        {
            if(v==fa[u]) continue;
            if(lca(v,capital)==v)
            {
                x=v;
                break;
            }
        }
        ll ans=(1ll<<31);
        ans=min(ans,query(1,1,start[x]-1,1,n));
        ans=min(ans,query(1,start[x]+num[x],n,1,n));
        return ans;
    }
    else return query(1,start[u],start[u]+num[u]-1,1,n);
}
//也可以使用倍增的方法找 v ,不过要将dfs1中的fa[u]变成fa[u][i]这个数组
int qTree2(int u)
{
    if(u==capital) return query(1,start[1],start[1]+num[1]-1,1,n);
    else if(lca(u,capital)==u)
    {
        int v=capital;
        int dis=depth[capital]-depth[u]-1;
        for(int i=0;i<=20;++i)
            if(dis>>i&1)
                v=fa[v][i];
        ll ans=(1ll<<31);
        ans=min(ans,query(1,1,start[v]-1,1,n));
        ans=min(ans,query(1,start[v]+num[v],n,1,n));
        return ans;
    }
    else return query(1,start[u],start[u]+num[u]-1,1,n);
}
int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n-1;++i)
    {
        int u,v;
        scanf("%d%d",&u,&v);
        e[u].push_back(v);
        e[v].push_back(u);
    }
    for(int i=1;i<=n;++i) scanf("%d",&w[i]);
    scanf("%d",&capital);
    dfs1(1);
    dfs2(1,1);
    build(1,1,n);
    int op,id,x,y,v;

    while(m--)
    {
        scanf("%d",&op);
        if(op==1)
        {
            scanf("%d",&id);
            capital=id;
        }
        else if(op==2)
        {
            scanf("%d%d%d",&x,&y,&v);
            updPath(x,y,v);
        }
        else if(op==3)
        {
            scanf("%d",&id);
            printf("%d\n",qTree(id));
        }
    }
    return 0;
}

你可能感兴趣的:(图论,树链剖分)