Liang9

导航

树链剖分题解

P3379 LCA模板

#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N=5e5+10;
int n,m,root,mod;
int h[N],e[N<<1],ne[N<<1],idx;
int dep[N],top[N],id[N],cnt,fa[N],son[N],sz[N];

struct node
{
    int add,sum;
    int l,r;
}tr[N<<4];

void add(int a,int b)
{
    e[idx]=b,ne[idx]=h[a],h[a]=idx++;
}

void dfs1(int u,int f,int depth)
{
    dep[u]=depth,fa[u]=f;
    sz[u]=1;
    for(int i=h[u];~i;i=ne[i])
    {
        int v=e[i];
        if(v==f) continue;
        dfs1(v,u,depth+1);
        sz[u]+=sz[v];
        if(sz[v]>sz[son[u]]) son[u]=v;
    }
}

void dfs2(int u,int t)
{
    top[u]=t;
    if(!son[u]) return ;
    dfs2(son[u],t);
    for(int i=h[u];~i;i=ne[i])
    {
        int v=e[i];
        if(v==fa[u]||v==son[u]) continue;
        dfs2(v,v);
    }
}

signed main()
{
    cin.tie(0);
    cout.tie(0);
    ios::sync_with_stdio(0);
    cin>>n>>m>>root;
    memset(h,-1,sizeof h);
    for(int i=1;i<n;i++)
    {
        int u,v;
        cin>>u>>v;
        add(u,v),add(v,u);
    }
    
    dfs1(root,-1,1);
    dfs2(root,root);
    
    for(int i=1;i<=m;i++)
    {
       int u,v;
       cin>>u>>v;
       while(top[u]!=top[v])
       {
           if(dep[top[u]]<dep[top[v]]) v=fa[top[v]];
           else u=fa[top[u]];
       }
       if(dep[u]<dep[v]) cout<<u<<'\n';
       else cout<<v<<'\n';
    }
}

P3384 树链剖分模板

#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N=1e5+10;
int n,m,root,mod;
int h[N],e[N<<1],ne[N<<1],idx;
int dep[N],top[N],id[N],cnt,fa[N],son[N],sz[N],w[N],nw[N];

struct node
{
    int add,sum;
    int l,r;
}tr[N<<4];

void add(int a,int b)
{
    e[idx]=b,ne[idx]=h[a],h[a]=idx++;
}

void dfs1(int u,int f,int depth)
{
    dep[u]=depth,fa[u]=f;
    sz[u]=1;
    for(int i=h[u];~i;i=ne[i])
    {
        int v=e[i];
        if(v==f) continue;
        dfs1(v,u,depth+1);
        sz[u]+=sz[v];
        if(sz[v]>sz[son[u]]) son[u]=v;
    }
}

void dfs2(int u,int t)
{
    top[u]=t;
    id[u]=++cnt,nw[cnt]=w[u];
    if(!son[u]) return ;
    dfs2(son[u],t);
    for(int i=h[u];~i;i=ne[i])
    {
        int v=e[i];
        if(v==fa[u]||v==son[u]) continue;
        dfs2(v,v);
    }
}

void pushup(int u)
{
    tr[u].sum=(tr[u<<1].sum+tr[u<<1|1].sum)%mod;
}

void pushdown(int u)
{
    if(tr[u].add)
    {
        tr[u<<1].add+=tr[u].add,tr[u<<1|1].add+=tr[u].add;
        (tr[u<<1].sum+=tr[u].add*(tr[u<<1].r-tr[u<<1].l+1))%=mod;
        (tr[u<<1|1].sum+=tr[u].add*(tr[u<<1|1].r-tr[u<<1|1].l+1))%=mod;
        tr[u].add=0;
    }
}

void build(int u,int l,int r)
{
    tr[u].l=l,tr[u].r=r;
    if(l==r)
    {
        tr[u].add=0,tr[u].sum=nw[l]%mod;
        return ;
    }
    int mid=l+r>>1;
    build(u<<1,l,mid),build(u<<1|1,mid+1,r);
    pushup(u);
}

void modify(int u,int l,int r,int k)
{
    if(tr[u].l>=l&&tr[u].r<=r)
    {
        tr[u].add+=k,tr[u].sum=(tr[u].sum+k*(tr[u].r-tr[u].l+1))%mod;
        return ;
    }
    pushdown(u);
    int mid=tr[u].l+tr[u].r>>1;
    if(l<=mid) modify(u<<1,l,r,k);
    if(r>mid) modify(u<<1|1,l,r,k);
    pushup(u);
}

void modify_path(int u,int v,int k)//修改u->v路径上的点
{
    while(top[u]!=top[v])
    {
        if(dep[top[u]]<dep[top[v]]) swap(u,v);
        modify(1,id[top[u]],id[u],k);
        u=fa[top[u]];
    }
    if(dep[u]<dep[v]) swap(u,v);
    modify(1,id[v],id[u],k);
}

int query(int u,int l,int r)
{
    if(tr[u].l>=l&&tr[u].r<=r)
    {
        return  tr[u].sum%mod;
    }
    pushdown(u);
    int mid=tr[u].l+tr[u].r>>1;
    int res=0;
    if(l<=mid) res=(res+query(u<<1,l,r))%mod;
    if(r>mid) res=(res+query(u<<1|1,l,r))%mod;
    return res;
}

int query_path(int u,int v)
{
    int res=0;
    while(top[u]!=top[v])
    {
        if(dep[top[u]]<dep[top[v]]) swap(u,v);
        res=(res+query(1,id[top[u]],id[u]))%mod;
        u=fa[top[u]];
    }
    if(dep[u]<dep[v]) swap(u,v);
    res=(res+query(1,id[v],id[u]))%mod;
    return res;
}

void modify_tree(int u,int k)
{
    modify(1,id[u],id[u]+sz[u]-1,k);
}

int query_tree(int u)
{
    return query(1,id[u],id[u]+sz[u]-1);
}

signed main()
{
    cin.tie(0);
    cout.tie(0);
    ios::sync_with_stdio(0);
    cin>>n>>m>>root>>mod;
    for(int i=1;i<=n;i++) cin>>w[i],w[i]%=mod;
    memset(h,-1,sizeof h);
    for(int i=1;i<n;i++)
    {
        int u,v;
        cin>>u>>v;
        add(u,v),add(v,u);
    }
    
    dfs1(root,-1,1);
    dfs2(root,root);
    
    build(1,1,n);
    
    for(int i=1;i<=m;i++)
    {
        int op,x,y,k;
        cin>>op>>x;
        if(op==1)
        {
            cin>>y>>k;
            modify_path(x,y,k);
        }else if(op==2)
        {
            cin>>y;
            cout<<query_path(x,y)<<'\n';
        }else if(op==3)
        {
            cin>>k;
            modify_tree(x,k);
        }else {
            cout<<query_tree(x)<<'\n';
        }
    }
}

P3178 树上操作

树链剖分模板
稍微修改一下就可以通过

#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N=1e5+10;
int n,m;
int h[N],e[N<<1],ne[N<<1],idx;
int dep[N],top[N],id[N],cnt,fa[N],son[N],sz[N],w[N],nw[N];

struct node
{
    int add,sum;
    int l,r;
}tr[N<<4];

void add(int a,int b)
{
    e[idx]=b,ne[idx]=h[a],h[a]=idx++;
}

void dfs1(int u,int f,int depth)
{
    dep[u]=depth,fa[u]=f;
    sz[u]=1;
    for(int i=h[u];~i;i=ne[i])
    {
        int v=e[i];
        if(v==f) continue;
        dfs1(v,u,depth+1);
        sz[u]+=sz[v];
        if(sz[v]>sz[son[u]]) son[u]=v;
    }
}

void dfs2(int u,int t)
{
    top[u]=t;
    id[u]=++cnt,nw[cnt]=w[u];
    if(!son[u]) return ;
    dfs2(son[u],t);
    for(int i=h[u];~i;i=ne[i])
    {
        int v=e[i];
        if(v==fa[u]||v==son[u]) continue;
        dfs2(v,v);
    }
}

void pushup(int u)
{
    tr[u].sum=(tr[u<<1].sum+tr[u<<1|1].sum);
}

void pushdown(int u)
{
    if(tr[u].add)
    {
        tr[u<<1].add+=tr[u].add,tr[u<<1|1].add+=tr[u].add;
        tr[u<<1].sum+=tr[u].add*(tr[u<<1].r-tr[u<<1].l+1);
        tr[u<<1|1].sum+=tr[u].add*(tr[u<<1|1].r-tr[u<<1|1].l+1);
        tr[u].add=0;
    }
}

void build(int u,int l,int r)
{
    tr[u].l=l,tr[u].r=r;
    if(l==r)
    {
        tr[u].add=0,tr[u].sum=nw[l];
        return ;
    }
    int mid=l+r>>1;
    build(u<<1,l,mid),build(u<<1|1,mid+1,r);
    pushup(u);
}

void modify(int u,int l,int r,int k)
{
    if(tr[u].l>=l&&tr[u].r<=r)
    {
        tr[u].add+=k,tr[u].sum=(tr[u].sum+k*(tr[u].r-tr[u].l+1));
        return ;
    }
    pushdown(u);
    int mid=tr[u].l+tr[u].r>>1;
    if(l<=mid) modify(u<<1,l,r,k);
    if(r>mid) modify(u<<1|1,l,r,k);
    pushup(u);
}

int query(int u,int l,int r)
{
    if(tr[u].l>=l&&tr[u].r<=r)
    {
        return  tr[u].sum;
    }
    pushdown(u);
    int mid=tr[u].l+tr[u].r>>1;
    int res=0;
    if(l<=mid) res=(res+query(u<<1,l,r));
    if(r>mid) res=(res+query(u<<1|1,l,r));
    return res;
}

int query_path(int u,int v)
{
    int res=0;
    while(top[u]!=top[v])
    {
        if(dep[top[u]]<dep[top[v]]) swap(u,v);
        res=(res+query(1,id[top[u]],id[u]));
        u=fa[top[u]];
    }
    if(dep[u]<dep[v]) swap(u,v);
    res=(res+query(1,id[v],id[u]));
    return res;
}

void modify_tree(int u,int k)
{
    modify(1,id[u],id[u]+sz[u]-1,k);
}


signed main()
{
    cin.tie(0);
    cout.tie(0);
    ios::sync_with_stdio(0);
    cin>>n>>m;
    for(int i=1;i<=n;i++) cin>>w[i];
    memset(h,-1,sizeof h);
    for(int i=1;i<n;i++)
    {
        int u,v;
        cin>>u>>v;
        add(u,v),add(v,u);
    }
    
    dfs1(1,0,1);
    dfs2(1,1);
    
    build(1,1,n);
    
    for(int i=1;i<=m;i++)
    {
        int op,x,k;
        cin>>op>>x;
        if(op==1)
        {
            cin>>k;
            modify(1,id[x],id[x],k);
        }else if(op==3)
        {
            cout<<query_path(x,1)<<'\n';
        }else 
        {
            cin>>k;
            modify_tree(x,k);
        }
    }
}

P3128 Max Flow P

一条输送路径的两端点需要修改,最终查询点的最大值
可以用线段树维护,单点修改,区间查询
但是这道题有更简单的方法
树上差分
每次修改相当于在\(s\)\(t\)上增加,在 \(lca\)\(fa[lca]\)上删除
所以这道题中树链剖分起到求\(lca\)的作用

#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N=5e4+10,M=1e5+10;
int h[N],e[M],ne[M],idx;
int n,k,stp;
int s[N];
int dep[N],fa[N],top[N],son[N],id[N],sz[N];


void add(int a,int b)
{
    e[idx]=b,ne[idx]=h[a],h[a]=idx++;
}

void dfs1(int u,int f,int deep)
{
    fa[u]=f,dep[u]=deep,sz[u]=1;
    for(int i=h[u];~i;i=ne[i])
    {
        int v=e[i];
        if(v==f) continue;
        dfs1(v,u,deep+1);
        sz[u]+=sz[v];
        if(sz[v]>sz[son[u]]) son[u]=v;
    }
}

void dfs2(int u,int t)
{
    top[u]=t,id[u]=++stp;
    if(son[u]) dfs2(son[u],t);
    for(int i=h[u];~i;i=ne[i])
    {
        int v=e[i];
        if(v==son[u]||v==fa[u]) continue;
        dfs2(v,v);
    }
}

int lca(int u,int v)
{
    while(top[u]!=top[v])
    {
        if(dep[top[u]]<dep[top[v]]) swap(u,v);
        u=fa[top[u]];
    }
    
    if(dep[u]<dep[v]) swap(u,v);
    return v;
}

int dfs3(int u)
{
    int ans=0;
    for(int i=h[u];~i;i=ne[i])
    {
        int v=e[i];
        if(v==fa[u]) continue;
        ans=max(ans,dfs3(v));
        s[u]+=s[v];
    }
    return max(ans,s[u]);
}

signed main()
{
    cin>>n>>k;
    memset(h,-1,sizeof h);
    for(int i=1;i<n;i++)
    {
        int u,v;
        cin>>u>>v;
        add(u,v);
        add(v,u);
    }
    dfs1(1,0,1);
    dfs2(1,1);
    
    for(int i=1;i<=k;i++)
    {
        int u,v;
        cin>>u>>v;
        int l=lca(u,v);
        s[u]++,s[v]++;
        s[l]--,s[fa[l]]--;
    }
    
    cout<<dfs3(1)<<endl;
}

P3258 松鼠的新家

\(a_i\) 走到 \(a_{i+1}\),需要将路径上所有点 \(+1\)
用树上差分维护点权
代码和上一道几乎一样,但注意处理边界情况,把重复的减去
这里给出一个倍增求\(lca\)的做法

#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N=3e5+10,M=6e5+10;
int n;
int q[N],fa[N][31],num[N],dep[N];
int h[N],e[M],ne[M],idx;

void add(int a,int b)
{
    e[idx]=b,ne[idx]=h[a],h[a]=idx++;
}

void dfs(int u,int f)
{
    fa[u][0]=f,dep[u]=dep[f]+1;
    
    for(int i=1;i<=30;i++)
    fa[u][i]=fa[fa[u][i-1]][i-1];
    for(int i=h[u];~i;i=ne[i])
    {
        int v=e[i];
        if(v==f) continue;
        dfs(v,u);
    }
}

int lca(int u,int v)
{
    if(dep[u]<dep[v]) swap(u,v);
    for(int i=30;i>=0;i--)
    {
        if(dep[fa[u][i]]>=dep[v]) u=fa[u][i];
    }
    if(v==u) return v;
    for(int i=30;i>=0;i--)
    {
        if(fa[u][i]!=fa[v][i]) u=fa[u][i],v=fa[v][i];
    }
    
    return fa[v][0];
}

void query(int u,int f)
{
    for(int i=h[u];~i;i=ne[i])
    {
        int v=e[i];
        if(v==f) continue;
        query(v,u);
        num[u]+=num[v];
    }
}

signed main()
{
    cin>>n;
    memset(h,-1,sizeof h);
    for(int i=1;i<=n;i++) cin>>q[i];
    for(int i=1;i<n;i++)
    {
        int u,v;
        cin>>u>>v;
        add(u,v);
        add(v,u);
    }
    dfs(1,0);
    
    for(int i=2;i<=n;i++)
    {
        int u=q[i-1],v=q[i];
        int t=lca(u,v);
        num[fa[t][0]]--;
        num[t]--;
        num[u]++;
        num[v]++;
    }
    
    
    query(1,0);
    for(int i=2;i<=n;i++) num[q[i]]--;
    
    for(int i=1;i<=n;i++) cout<<num[i]<<'\n';
}

P2146 软件包管理器

安装 就相当于查询并修改 \(x\) 到根的路径上有几个点未添加
卸载就相当于查询并修改以 \(x\) 为根的子树中有几个点已经添加
可以用树链剖分+线段树维护

#include <bits/stdc++.h>
using namespace std;
const int N=1e5+10;
int n,q;
vector<int> e[N];
int fa[N];
int top[N],son[N],cnt;
int dep[N],id[N],sz[N];
struct noe
{
    int l,r;
    int sum,add;
}tr[N<<2];

void dfs1(int u,int f,int depth)
{
    sz[u]=1,dep[u]=depth;
    // id[u]=++cnt;
    son[u]=n+1;
    for(int i=0;i<e[u].size();i++)
    {
        int v=e[u][i];
        dfs1(v,u,depth+1);
        sz[u]+=sz[v];
        if(sz[v]>sz[son[u]]) son[u]=v;
    }
}

void dfs2(int u,int t)
{
    top[u]=t;
    id[u]=++cnt;
    if(son[u]==n+1) return ;
    dfs2(son[u],t);
    for(int i=0;i<e[u].size();i++)
    {
        int v=e[u][i];
        if(v==fa[u]||v==son[u]) continue;
        dfs2(v,v);
    }
}

void pushup(int u)
{
    tr[u].sum=tr[u<<1].sum+tr[u<<1|1].sum;
    
}

void pushdown(int u)
{
    if(tr[u].add!=0)
    {
        tr[u<<1].add=tr[u].add,tr[u<<1].sum=max((int)0,(tr[u<<1].r-tr[u<<1].l+1)*tr[u].add);
        tr[u<<1|1].add=tr[u].add,tr[u<<1|1].sum=max((tr[u<<1|1].r-tr[u<<1|1].l+1)*tr[u].add,(int)0);
        tr[u].add=0;
    }
}

void build(int u,int l,int r)
{
    tr[u].l=l,tr[u].r=r;
    tr[u].add=0,tr[u].sum=0;
    if(l==r)
    {
        return ;
    }
    int mid=l+r>>1;
    build(u<<1,l,mid),build(u<<1|1,mid+1,r);
}

void modify(int u,int l,int r,int k)
{
    if(tr[u].l>=l&&tr[u].r<=r)
    {
        if(k==1)
        tr[u].add=1,tr[u].sum=tr[u].r-tr[u].l+1;
        else tr[u].add=-1,tr[u].sum=0;
        return ;
    }
    pushdown(u);
    int mid=tr[u].l+tr[u].r>>1;
    if(l<=mid) modify(u<<1,l,r,k);
    if(r>mid) modify(u<<1|1,l,r,k);
    pushup(u);
}

int query(int u,int l,int r,int x)
{
    if(tr[u].l>=l&&tr[u].r<=r) {
        // if(x==1) cout<<tr[u].l<<' '<<tr[u].r<<' '<<tr[u].sum<<endl;
        if(x==1) return tr[u].sum;//有几个点被安装了
        else return (tr[u].r-tr[u].l+1-tr[u].sum);//没被安装的数量
    }
    
    pushdown(u);
    int res=0;
    int mid=tr[u].l+tr[u].r>>1;
    if(l<=mid) res+=query(u<<1,l,r,x);
    if(r>mid) res+=query(u<<1|1,l,r,x);
    
    // if(x==1) cout<<res<<endl;
    return res;
}

int query_path(int v,int u)
{
    if(query(1,id[u],id[u],1)==1) return 0;
    int res=0;
    while(top[u]!=top[v])
    {
        if(dep[top[u]]<dep[top[v]]) swap(u,v);
        res+=query(1,id[top[u]],id[u],0);//路径上没加过的点
        u=fa[top[u]];
    }
    
    if(dep[u]<dep[v]) swap(u,v);
    res+=query(1,id[v],id[u],0);
    // cout<<u<<' '<<v<<' '<<id[u]<<' '<<id[v]<<endl;
    return res;
}

int query_tree(int u)
{
    return query(1,id[u],id[u]+sz[u]-1,1);
}

void modify_path(int v,int u)
{
    if(query(1,id[u],id[u],1)==1) return ;
    while(top[u]!=top[v])
    {
        if(dep[top[u]]<dep[top[v]]) swap(u,v);
        modify(1,id[top[u]],id[u],1);
        u=fa[top[u]];
    }
    
    if(dep[u]<dep[v]) swap(u,v);
    modify(1,id[v],id[u],1);
}

void modify_tree(int u)
{
    modify(1,id[u],id[u]+sz[u]-1,-1);
}

signed main()
{
    cin>>n;
    for(int i=1;i<n;i++)
    {
        cin>>fa[i];
        e[fa[i]].push_back(i);
    }
    
    dfs1(0,-1,1);
    dfs2(0,0);
    build(1,1,n);
    
    // for(int i=0;i<=n-1;i++) cout<<id[i]<<' ';
    
    cin>>q;
    for(int i=1;i<=q;i++)
    {
        string op;
        int u;
        cin>>op>>u;
        if(op=="install")
        {
            cout<<query_path(0,u)<<endl;
            modify_path(0,u);
        }else {
            cout<<query_tree(u)<<endl;
            modify_tree(u);
        }
    }
}

P3313 旅行

树剖+动态开点线段树
以不同宗教为根,建立线段树

#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N=1e5+10;
int n,q;
int w[N],c[N];
int h[N],e[N<<1],ne[N<<1],idx;
int dep[N],id[N],cnt,sz[N],fa[N],son[N],top[N];
int root[N],tot;

struct node
{
    int l,r;
    int sum,maxn;
}tr[20000002];

void add(int a,int b)
{
    e[idx]=b,ne[idx]=h[a],h[a]=idx++;
}

void dfs1(int u,int f,int depth)
{
    fa[u]=f,dep[u]=depth,sz[u]=1;
    for(int i=h[u];~i;i=ne[i])
    {
        int v=e[i];
        if(v==f) continue;
        dfs1(v,u,depth+1);
        sz[u]+=sz[v];
        if(sz[v]>sz[son[u]]) son[u]=v;
    }
}

void dfs2(int u,int t)
{
    top[u]=t;
    id[u]=++cnt;
    if(!son[u]) return ;
    dfs2(son[u],t);
    for(int i=h[u];~i;i=ne[i])
    {
        int v=e[i];
        if(v==fa[u]||v==son[u]) continue;
        dfs2(v,v);
    }
}

void modify(int &u,int l,int r,int w,int pos)
{
    if(!u) u=++tot;
    tr[u].maxn=max(tr[u].maxn,w);
    tr[u].sum+=w;
    if(l==r) return ;
    int mid=l+r>>1;
    if(pos<=mid) modify(tr[u].l,l,mid,w,pos);
    else modify(tr[u].r,mid+1,r,w,pos);
}

void pushup(int u)
{
    tr[u].maxn=max(tr[tr[u].l].maxn,tr[tr[u].r].maxn);
    tr[u].sum=tr[tr[u].l].sum+tr[tr[u].r].sum;
    return ;
}

void remove(int u,int l,int r,int w,int pos)
{
    if(l==r) {
        tr[u].maxn=0,tr[u].sum=0;
        return ;
    }
    
    int mid=l+r>>1;
    if(pos<=mid) remove(tr[u].l,l,mid,w,pos);
    else remove(tr[u].r,mid+1,r,w,pos);
    pushup(u);
}

int query1(int u,int L,int R,int l,int r)
{
    if(L>=l&&R<=r) return tr[u].sum;
    int mid=L+R>>1;
    int res=0;
    if(l<=mid) res+=query1(tr[u].l,L,mid,l,r);
    if(r>mid) res+=query1(tr[u].r,mid+1,R,l,r);
    return res;
}

int query2(int u,int L,int R,int l,int r)
{
    if(L>=l&&R<=r) return tr[u].maxn;
    int mid=L+R>>1;
    int res=0;
    if(l<=mid) res=max(res,query2(tr[u].l,L,mid,l,r));
    if(r>mid) res=max(res,query2(tr[u].r,mid+1,R,l,r));
    return res;
}

int query_sum(int u,int v,int c)
{
    int res=0;
    while(top[u]!=top[v])
    {
        if(dep[top[u]]<dep[top[v]]) swap(u,v);
        res+=query1(root[c],1,n,id[top[u]],id[u]);
        u=fa[top[u]];
    }
    
    if(dep[u]<dep[v]) swap(u,v);
    res+=query1(root[c],1,n,id[v],id[u]);
    return res;
}

int query_max(int u,int v,int c)
{
    int res=0;
    while(top[u]!=top[v])
    {
        if(dep[top[u]]<dep[top[v]]) swap(u,v);
        res=max(res,query2(root[c],1,n,id[top[u]],id[u]));
        u=fa[top[u]];
    }
    if(dep[u]<dep[v]) swap(u,v);
    res=max(res,query2(root[c],1,n,id[v],id[u]));
    return res;
}

signed main()
{
    cin>>n>>q;
    memset(h,-1,sizeof h);
    for(int i=1;i<=n;i++) cin>>w[i]>>c[i];
    
    for(int i=1;i<n;i++)
    {
        int u,v;
        cin>>u>>v;
        add(u,v),add(v,u);
    }
    
    dfs1(1,-1,1);
    dfs2(1,1);
    
    for(int i=1;i<=n;i++)
    {
        modify(root[c[i]],1,n,w[i],id[i]);
    }
    
    for(int i=1;i<=q;i++)
    {
        string op;
        int x,y;
        cin>>op>>x>>y;
        if(op=="CC")
        {
            remove(root[c[x]],1,n,w[x],id[x]);
            modify(root[y],1,n,w[x],id[x]);
            c[x]=y;
        }else if(op=="CW")
        {
            remove(root[c[x]],1,n,w[x],id[x]);
            modify(root[c[x]],1,n,y,id[x]);
            w[x]=y;
        }
        else if(op=="QS")
        {
            cout<<query_sum(x,y,c[x])<<endl;
        }else {
            cout<<query_max(x,y,c[x])<<endl;
        }
    }
}

CF1062E Company

用线段树维护区间 \(dfs\) 序最大、次大、最小、次小值
树剖求 \(lca\),再进行比较选择最优解

#include <bits/stdc++.h>
using namespace std;
const int N=1e5+10;
int n,m;
int dfn[N],id[N],stp,dep[N],son[N],sz[N],top[N];
int fa[N];
vector<int> e[N];
 
struct lyw
{
    int l,r;
    int a1,a2,b1,b2;//最大,次大,最小,次小
}tr[N<<2];
 
void pushup(lyw &a,lyw b,lyw c)
{
    if(b.a1>c.a1)
    {
        a.a1=b.a1;
        a.a2=max(b.a2,c.a1);
    }else {
        a.a1=c.a1;
        a.a2=max(b.a1,c.a2);
    }
    if(b.b1<c.b1)
    {
        a.b1=b.b1;
        a.b2=min(b.b2,c.b1);
    }else {
        a.b1=c.b1;
        a.b2=min(b.b1,c.b2);
    }
    
}
 
void dfs1(int u,int d)
{
    dep[u]=d;
    sz[u]=1;
    for(auto v:e[u])
    {
        dfs1(v,d+1);
        sz[u]+=sz[v];
        if(sz[v]>sz[son[u]]) son[u]=v;
    }
}
 
void dfs2(int u,int t)
{
    top[u]=t;
    dfn[u]=++stp;
    id[stp]=u;
    if(son[u]) dfs2(son[u],t);
    for(auto v:e[u])
    {
        if(v==son[u]) continue;
        dfs2(v,v);
    }
}
 
void build(int u,int l,int r)
{
    tr[u].l=l,tr[u].r=r;
    if(l==r)
    {
        tr[u].a1=tr[u].b1=dfn[l];
        tr[u].a2=0;
        tr[u].b2=n+1;
        return ;
    }
    
    int mid=l+r>>1;
    build(u<<1,l,mid),build(u<<1|1,mid+1,r);
    pushup(tr[u],tr[u<<1],tr[u<<1|1]);
}
 
lyw query(int u,int l,int r)
{
    if(tr[u].r<l||tr[u].l>r) return (lyw){0,0,0,0,n+1,n+1};
    if(tr[u].l>=l&&tr[u].r<=r) return tr[u];
    
    int mid=l+r>>1;
    lyw ans;
    if(r<=mid) return query(u<<1,l,r);
    else if(l>mid) return query(u<<1|1,l,r);
    else {
        pushup(ans,query(u<<1,l,r),query(u<<1|1,l,r));
        return ans;
    }
}
 
int lca(int u,int v)
{
    if(u==0||v==0) return 0;
    while(top[u]!=top[v])
    {
        if(dep[top[u]]<dep[top[v]]) v=fa[top[v]];
        else u=fa[top[u]];
    }
    if(dep[u]<dep[v]) return u;
    else return v;
}
 
signed main()
{
    cin>>n>>m;
    for(int i=2;i<=n;i++)
    {
        cin>>fa[i];
        e[fa[i]].push_back(i);
    }
    fa[1]=0;
    dfs1(1,0);
    dfs2(1,1);
    
    build(1,1,n);
    for(int i=1;i<=m;i++)
    {
        int l,r;
        cin>>l>>r;
        if(l==r-1)
        {
            if(dep[l]>dep[r]) cout<<r<<' '<<dep[l]<<'\n';
            else cout<<l<<' '<<dep[r]<<'\n';
            continue;
        }
        lyw w=query(1,l,r);
        int l1=lca(id[w.a1],id[w.b2]);
        int l2=lca(id[w.b1],id[w.a2]);
        if(dep[l1]<dep[l2]) cout<<id[w.a1]<<' '<<dep[l2]<<'\n';
        else cout<<id[w.b1]<<' '<<dep[l1]<<'\n';
    }
}

P6071 Treequery

上一个问题的加强版
针对 \(p\)\(lca\) 的位置关系分类讨论
主席树 \(+\) 树链剖分

  • 调试警告:大数据TLE了,可能是数组开小了

详细题解见这里

#include <bits/stdc++.h>
using namespace std;
const int N=2e5+50,M=4e5+10;
int n,q;
int h[N],e[M],ne[M],w[M],idx;
int fa[N],sum[N],sz[N],dep[N],son[N],top[N],num[N],id[N],stp,tot;
int lc[N*40],rc[N*40],root[N];
int a[N<<2];


void add(int a,int b,int c)
{
    e[idx]=b,ne[idx]=h[a],w[idx]=c,h[a]=idx++;
}

int addtr(int pre,int l,int r,int x)
{
    int o=++tot;
    if(l<r)
    {
        int mid=l+r>>1;
        if(x<=mid) lc[o]=addtr(lc[pre],l,mid,x),rc[o]=rc[pre];
        else lc[o]=lc[pre],rc[o]=addtr(rc[pre],mid+1,r,x);
    }
    return o;
}

int merge(int u,int v,int l,int r)
{
    if(!u||!v) return u|v;
    int o=++tot;
    if(l<r)
    {
        int mid=l+r>>1;
        lc[o]=merge(lc[u],lc[v],l,mid);
        rc[o]=merge(rc[u],rc[v],mid+1,r);
    }
    return o;
}

bool ask(int u,int l,int r,int L,int R)
{
    if(!u) return 0;
    int mid=L+R>>1;
    if(l<=L&&r>=R) return 1;
    if(l<=mid&&ask(lc[u],l,r,L,mid)) return 1;
    else return r>mid&&ask(rc[u],l,r,mid+1,R);
}

void dfs1(int u,int f)
{
    sz[u]=1,fa[u]=f,dep[u]=dep[f]+1;
    root[u]=addtr(0,1,n,u);
    for(int i=h[u];~i;i=ne[i])
    {
        int v=e[i];
        if(v==f) continue;
        sum[v]=sum[u]+w[i];
        dfs1(v,u);
        sz[u]+=sz[v];
        if(sz[v]>sz[son[u]]) son[u]=v;
        root[u]=merge(root[u],root[v],1,n);
    }
}

void dfs2(int u,int t)
{
    top[u]=t;
    id[u]=++stp,num[stp]=u;
    if(son[u]) dfs2(son[u],t);
    for(int i=h[u];~i;i=ne[i])
    {
        int v=e[i];
        if(v==fa[u]||v==son[u]) continue;
        dfs2(v,v);
    }
}

int lca(int u,int v)
{
    while(top[u]!=top[v])
    {
        if(dep[top[u]]<dep[top[v]]) v=fa[top[v]];
        else u=fa[top[u]];
    }
    if(dep[u]<dep[v]) return u;
    else return v;
}

void build(int u,int l,int r)
{
    if(l==r)
    {
        a[u]=l;
        return ;
    }
    int mid=l+r>>1;
    build(u<<1,l,mid),build(u<<1|1,mid+1,r);
    a[u]=lca(a[u<<1],a[u<<1|1]);
}

int query(int u,int l,int r,int L,int R)
{
    if(l<=L&&r>=R) return a[u];
    int mid=L+R>>1;
    if(r<=mid) return query(u<<1,l,r,L,mid);
    else if(l>mid) return query(u<<1|1,l,r,mid+1,R);
    else return lca(query(u<<1,l,r,L,mid),query(u<<1|1,l,r,mid+1,R));
}

int getfa(int u,int x,int y)
{
    while(u)
    {
        if(ask(root[top[u]],x,y,1,n))
        {
            int l=id[top[u]],r=id[u];
            while(l<r)
            {
                int mid=l+r+1>>1;
                if(ask(root[num[mid]],x,y,1,n)) l=mid;
                else r=mid-1;
            }
            return num[l];
        }
        u=fa[top[u]];
    }
}

signed main()
{
    cin>>n>>q;
    memset(h,-1,sizeof h);
    for(int i=1;i<n;i++)
    {
        int u,v,w;
        cin>>u>>v>>w;
        add(u,v,w);
        add(v,u,w);
    }
    dfs1(1,0);
    dfs2(1,1);
    build(1,1,n);
    int ans=0;
    while(q--)
    {
        int p,l,r;
        cin>>p>>l>>r;
        p^=ans,l^=ans,r^=ans;
        int u=query(1,l,r,1,n),v;
        if(id[u]>=id[p]&&id[u]<=id[p]+sz[p]-1) ans=sum[u]-sum[p];
        else if(ask(root[p],l,r,1,n)) ans=0;
        else if(id[v=getfa(p,l,r)]>=id[u]&&id[v]<=id[u]+sz[u]-1) ans=sum[p]-sum[v];
        else ans=sum[p]+sum[u]-sum[v]*2;
        
        cout<<ans<<'\n';
    }
}

P4211 LCA

暴力不难写出,重点在于如何优化
注意到 \(lca\) 一定在 \(z\) 到根节点的路径上,所以通过 \(1-z\) 统计答案
对于点 \(x\),如果将 \(x\) 到根节点的路径上的点权全 \(+1\),每个点到根的 \(dep\) 就等于这个点到根节点路径上所有点的和
注意到这个性质,就可以差分求解了
树链剖分+线段树+差分
总时间复杂度\(O(nlog^2n)\)

#include <bits/stdc++.h>
using namespace std;
const int N=5e4+10,M=1e5+10,mod=201314;
int n,m,cnt;
int fa[N];
int h[N],ne[M],e[M],idx;
int dep[N],sz[N],son[N],top[N],id[N],stp;
int ans[N];

struct que
{
    int id,type;
    int u,z;
    
    bool operator<(const que &W) const
    {
        return u<W.u;
    }
}q[M];

struct lyw
{
    int l,r,sum,add;
}tr[N<<2];

void add(int a,int b)
{
    e[idx]=b,ne[idx]=h[a],h[a]=idx++;
}

void dfs1(int u,int d)
{
    dep[u]=d,sz[u]=1;
    for(int i=h[u];~i;i=ne[i])
    {
        int v=e[i];
        if(v==fa[u]) continue;
        dfs1(v,d+1);
        sz[u]+=sz[v];
        if(sz[v]>sz[son[u]]) son[u]=v;
    }
}

void dfs2(int u,int t)
{
    top[u]=t,id[u]=++stp;
    if(son[u]) dfs2(son[u],t);
    else return ;
    for(int i=h[u];~i;i=ne[i])
    {
        int v=e[i];
        if(v==fa[u]||v==son[u]) continue;
        dfs2(v,v);
    }
}

void build(int u,int l,int r)
{
    tr[u].l=l,tr[u].r=r,tr[u].sum=tr[u].add=0;
    if(l==r)
    {
        return ;
    }
    int mid=l+r>>1;
    build(u<<1,l,mid),build(u<<1|1,mid+1,r);
}

void pushdown(int u)
{
    if(tr[u].add)
    {
        tr[u<<1].add+=tr[u].add,tr[u<<1|1].add+=tr[u].add;
        tr[u<<1].sum+=tr[u].add*(tr[u<<1].r-tr[u<<1].l+1);
        tr[u<<1|1].sum+=tr[u].add*(tr[u<<1|1].r-tr[u<<1|1].l+1);
        tr[u].add=0;
    }
}

void pushup(int u)
{
    tr[u].sum=tr[u<<1].sum+tr[u<<1|1].sum;
}

void modify(int u,int l,int r)
{
    if(l<=tr[u].l&&r>=tr[u].r)
    {
        tr[u].add++;
        tr[u].sum+=(tr[u].r-tr[u].l+1);
        return ;
    }
    
    pushdown(u);
    int mid=tr[u].l+tr[u].r>>1;
    if(l<=mid) modify(u<<1,l,r);
    if(r>mid) modify(u<<1|1,l,r);
    pushup(u);
}

int query(int u,int l,int r)
{
    if(tr[u].l>=l&&tr[u].r<=r)
    {
        return tr[u].sum;
    }
    pushdown(u);
    int mid=tr[u].l+tr[u].r>>1;
    int res=0;
    if(l<=mid) res+=query(u<<1,l,r);
    if(r>mid) res+=query(u<<1|1,l,r);
    return res;
}

void modify_path(int u,int v)
{
    while(top[u]!=top[v])
    {
        if(dep[top[u]]<dep[top[v]]) swap(u,v);
        modify(1,id[top[u]],id[u]);
        u=fa[top[u]];
    }
    if(dep[u]<dep[v]) swap(u,v);
    modify(1,id[v],id[u]);
}

int query_path(int u,int v)
{
    int res=0;
    while(top[u]!=top[v])
    {
        if(dep[top[u]]<dep[top[v]]) swap(u,v);
        (res+=query(1,id[top[u]],id[u]))%=mod;
        u=fa[top[u]];
    }
    if(dep[u]<dep[v]) swap(u,v);
    (res+=query(1,id[v],id[u]))%=mod;
    return res;
}

signed main()
{
    cin>>n>>m;
    memset(h,-1,sizeof h);
    for(int i=2;i<=n;i++)
    {
        cin>>fa[i];
        fa[i]++;
        add(i,fa[i]);
        add(fa[i],i);
    }
    
    dfs1(1,1);
    dfs2(1,1);
    build(1,1,n);
    for(int i=1;i<=m;i++)
    {
        int l,r,z;
        cin>>l>>r>>z;
        l++,r++,z++;
        q[++cnt]={i,-1,l-1,z};
        q[++cnt]={i,1,r,z};
    }
    
    sort(q+1,q+1+cnt);
    
    for(int i=1,x=0;i<=cnt;i++)
    {
        while(x<q[i].u)
        {
            x++;
            modify_path(1,x);
        }
        
        ans[q[i].id]+=(q[i].type*query_path(1,q[i].z));
    }
    
    for(int i=1;i<=m;i++) cout<<(ans[i]+mod)%mod<<'\n';
}

P4216 情报传递

可以清楚查询操作相当于询问\(x-y\)路径上有几个点\(t<now-z+1\)
可以考虑把这个问题离线,按时间从小到大插入和查询
用树状数组查询树上差分

#include <bits/stdc++.h>
using namespace std;
const int N=2e5+10;
int n,m,root,cnt;
int fa[N],dep[N],ne[N<<1],e[N<<1],h[N],idx;
int son[N],sz[N],id[N],stp,top[N];
int ans1[N],ans2[N];
int tr[N];
struct lyw
{
    int id,type,t,x,y;
    bool operator<(const lyw &W) const
    {
        if(t!=W.t)
        return t<W.t;
        return type>W.type;//先插入,后查询
    }
}q[N];

void add(int a,int b)
{
    e[idx]=b,ne[idx]=h[a],h[a]=idx++;
}

void dfs1(int u,int d)
{
    dep[u]=d,sz[u]=1;
    for(int i=h[u];~i;i=ne[i])
    {
        int v=e[i];
        if(v==fa[u]) continue;
        dfs1(v,d+1);
        sz[u]+=sz[v];
        if(sz[v]>sz[son[u]]) son[u]=v;
    }
}

void dfs2(int u,int t)
{
    top[u]=t,id[u]=++stp;
    if(son[u]) dfs2(son[u],t);
    else return ;
    for(int i=h[u];~i;i=ne[i])
    {
        int v=e[i];
        if(v==fa[u]||v==son[u]) continue;
        dfs2(v,v);
    }
}

int lowbit(int x)
{
    return x&(-x);
}

void Add(int x,int c)
{
    for(int i=x;i<=n;i+=lowbit(i)) tr[i]+=c;
}

int query(int x)
{
    int res=0;
    for(int i=x;i>=1;i-=lowbit(i)) res+=tr[i];
    return res;
}

int lca(int u,int v)
{
    while(top[u]!=top[v])
    {
        if(dep[top[u]]<dep[top[v]]) swap(u,v);
        u=fa[top[u]];
    }
    if(dep[u]<dep[v]) return u;
    else return v;
}

void Query(int u,int v,int o)
{
    int l=lca(u,v);
    ans1[o]=dep[u]+dep[v]-dep[l]*2+1;
    ans2[o]=query(id[u])+query(id[v])-query(id[l])-query(id[fa[l]]);
}

signed main()
{
    cin>>n;
    memset(h,-1,sizeof h);
    for(int i=1;i<=n;i++)
    {
        cin>>fa[i];
        if(fa[i]==0) root=i;
        else add(i,fa[i]),add(fa[i],i);
    }
    dfs1(root,1);
    dfs2(root,root);
    
    cin>>m;
    for(int i=1;i<=m;i++)
    {
        int op;
        cin>>op;
        if(op==2)
        {
            int x;
            cin>>x;
            q[i]={i,2,i,x,0};
        }else {
            int x,y,z;
            cin>>x>>y>>z;
            q[i]={i,1,i-z-1,x,y};
        }
    }
    
    sort(q+1,q+1+m);
    
    for(int i=1;i<=m;i++)
    {
        if(q[i].type==2)
        {
            Add(id[q[i].x],1);
            Add(id[q[i].x]+sz[q[i].x],-1);
        }else {
            Query(q[i].x,q[i].y,q[i].id);
        }
    }
    
    for(int i=1;i<=m;i++)
    if(ans1[i]||ans2[i]) cout<<ans1[i]<<' '<<ans2[i]<<'\n';
}

P2486 染色

树链剖分+线段树
在线段树上储存段数,最左边的颜色,最右边的颜色,是否被修改过
路径上操作需要额外特判有没有可以合并的

#include <bits/stdc++.h>
using namespace std;
const int N=1e5+10;
int n,m;
int w[N],id[N],nw[N],dep[N],fa[N],top[N],son[N],sz[N],stp;
int e[N<<1],ne[N<<1],h[N],idx;

struct lyw
{
    int l,r;
    int sum,lc,rc,tag;
}tr[N<<2];

void add(int a,int b)
{
    e[idx]=b,ne[idx]=h[a],h[a]=idx++;
}

void dfs1(int u,int f,int d)
{
    dep[u]=d,fa[u]=f,sz[u]=1;
    for(int i=h[u];~i;i=ne[i])
    {
        int v=e[i];
        if(v==f) continue;
        dfs1(v,u,d+1);
        sz[u]+=sz[v];
        if(sz[v]>sz[son[u]]) son[u]=v;
    }
}

void dfs2(int u,int t)
{
    top[u]=t,id[u]=++stp,nw[stp]=w[u];
    if(son[u]) dfs2(son[u],t);
    else return ;
    for(int i=h[u];~i;i=ne[i])
    {
        int v=e[i];
        if(v==fa[u]||v==son[u]) continue;
        dfs2(v,v);
    }
}

void pushup(lyw &a,lyw b,lyw c)
{
    a.sum=b.sum+c.sum;
    if(b.rc==c.lc) a.sum--;
    a.lc=b.lc,a.rc=c.rc;
}

void build(int u,int l,int r)
{
    tr[u].l=l,tr[u].r=r;
    if(l==r)
    {
        tr[u].sum=1,tr[u].lc=tr[u].rc=nw[l];
        return ;
    }
    int mid=l+r>>1;
    build(u<<1,l,mid);
    build(u<<1|1,mid+1,r);
    pushup(tr[u],tr[u<<1],tr[u<<1|1]);
}

void pushdown(int u)
{
    if(tr[u].tag)
    {
        tr[u<<1].tag=tr[u<<1|1].tag=tr[u].tag;
        tr[u<<1].sum=tr[u<<1|1].sum=1;
        tr[u<<1].lc=tr[u<<1|1].lc=tr[u].lc;
        tr[u<<1].rc=tr[u<<1|1].rc=tr[u].rc;
        tr[u].tag=0;
    }
}

void modify(int u,int l,int r,int val)
{
    if(tr[u].l>=l&&tr[u].r<=r)
    {
        tr[u].tag=val;
        tr[u].sum=1,tr[u].lc=tr[u].rc=val;
        return ;
    }
    pushdown(u);
    int mid=tr[u].l+tr[u].r>>1;
    if(l<=mid) modify(u<<1,l,r,val);
    if(r>mid) modify(u<<1|1,l,r,val);
    pushup(tr[u],tr[u<<1],tr[u<<1|1]);
}

lyw query(int u,int l,int r)
{
    if(tr[u].l>=l&&tr[u].r<=r) return tr[u];
    pushdown(u);
    int mid=tr[u].l+tr[u].r>>1;
    if(r<=mid) return query(u<<1,l,r);
    else if(l>mid) return query(u<<1|1,l,r);
    else {
        lyw res;
        pushup(res,query(u<<1,l,r),query(u<<1|1,l,r));
        return res;
    }
}

void modify_path(int u,int v,int c)
{
    while(top[u]!=top[v])
    {
        if(dep[top[u]]<dep[top[v]]) swap(u,v);
        modify(1,id[top[u]],id[u],c);
        u=fa[top[u]];
    }
    if(dep[u]<dep[v]) swap(u,v);
    modify(1,id[v],id[u],c);
}

int query_path(int u,int v)
{
    int ans1=-1,ans2=-1,ans=0;
    lyw c;
    while(top[u]!=top[v])
    {
        if(dep[top[u]]<dep[top[v]]) swap(u,v),swap(ans1,ans2);
        c=query(1,id[top[u]],id[u]);
        ans+=c.sum;
        if(ans1==c.rc) ans--;
        ans1=c.lc;
        u=fa[top[u]];
    }
    if(dep[u]<dep[v]) swap(u,v),swap(ans1,ans2);
    c=query(1,id[v],id[u]);
    ans+=c.sum;
    if(ans1==c.rc) ans--;
    if(ans2==c.lc) ans--;
    return ans;
}

signed main()
{
    cin>>n>>m;
    memset(h,-1,sizeof h);
    for(int i=1;i<=n;i++) cin>>w[i];
    for(int i=1;i<n;i++) {
        int u,v;
        cin>>u>>v;
        add(u,v);
        add(v,u);
    }
    
    dfs1(1,1,1);
    dfs2(1,1);
    
    build(1,1,n);
    
    // for(int i=1;i<=n;i++)
    // modify(1,id[i],id[i],w[i]);
    
    for(int i=1;i<=m;i++)
    {
        char op;
        int a,b,c;
        cin>>op>>a>>b;
        if(op=='C')
        {
            cin>>c;
            modify_path(a,b,c);
        }else
        {
            cout<<query_path(a,b)<<'\n';
        }
    }
}

P7735 轻重边

这道题的转化真的太妙了
建议好好思考一下再看题解
把改变边的状态,转化成改变点权
这样问题就转化成了
重边\(->\) 两端点权相同
轻边\(->\) 两端点权不同
每次修改给点权附一个独\(one\)\(two\)的数
查询\(->\) 路径上有多少值相同的相邻点对
\(by\) \(the\) \(way\) 这道题可以用染色的代码直接改
这题稍微有一点点卡常,记得开输入输出优化

#include <bits/stdc++.h>
using namespace std;
const int N=1e5+10;
int n,m;
int id[N],dep[N],fa[N],top[N],son[N],sz[N],stp;
int e[N<<1],ne[N<<1],h[N],idx;

struct lyw
{
    int l,r;
    int sum,lc,rc,tag;
}tr[N<<2];

void add(int a,int b)
{
    e[idx]=b,ne[idx]=h[a],h[a]=idx++;
}

void dfs1(int u,int f,int d)
{
    dep[u]=d,fa[u]=f,sz[u]=1;
    for(int i=h[u];~i;i=ne[i])
    {
        int v=e[i];
        if(v==f) continue;
        dfs1(v,u,d+1);
        sz[u]+=sz[v];
        if(sz[v]>sz[son[u]]) son[u]=v;
    }
}

void dfs2(int u,int t)
{
    top[u]=t,id[u]=++stp;
    if(son[u]) dfs2(son[u],t);
    else return ;
    for(int i=h[u];~i;i=ne[i])
    {
        int v=e[i];
        if(v==fa[u]||v==son[u]) continue;
        dfs2(v,v);
    }
}

void pushup(lyw &a,lyw b,lyw c)
{
    a.sum=b.sum+c.sum;
    if(b.rc==c.lc&&b.rc>0) a.sum++;
    a.lc=b.lc,a.rc=c.rc;
}

void build(int u,int l,int r)
{
    tr[u].l=l,tr[u].r=r;
    tr[u].tag=-1;
    if(l==r)
    {
        tr[u].sum=0,tr[u].lc=tr[u].rc=-u;
        return ;
    }
    int mid=l+r>>1;
    build(u<<1,l,mid);
    build(u<<1|1,mid+1,r);
    pushup(tr[u],tr[u<<1],tr[u<<1|1]);
}

void pushdown(int u)
{
    if(tr[u].tag!=-1)
    {
        tr[u<<1].tag=tr[u<<1|1].tag=tr[u].tag;
        tr[u<<1].sum=tr[u<<1].r-tr[u<<1].l;
        tr[u<<1|1].sum=tr[u<<1|1].r-tr[u<<1|1].l;
        tr[u<<1].lc=tr[u<<1|1].lc=tr[u].tag;
        tr[u<<1].rc=tr[u<<1|1].rc=tr[u].tag;
        tr[u].tag=-1;
    }
}

void modify(int u,int l,int r,int val)
{
    if(tr[u].l>=l&&tr[u].r<=r)
    {
        tr[u].tag=val;
        tr[u].sum=tr[u].r-tr[u].l,tr[u].lc=tr[u].rc=val;
        return ;
    }
    pushdown(u);
    int mid=tr[u].l+tr[u].r>>1;
    if(l<=mid) modify(u<<1,l,r,val);
    if(r>mid) modify(u<<1|1,l,r,val);
    pushup(tr[u],tr[u<<1],tr[u<<1|1]);
}

lyw query(int u,int l,int r)
{
    if(tr[u].l>=l&&tr[u].r<=r) return tr[u];
    pushdown(u);
    int mid=tr[u].l+tr[u].r>>1;
    if(r<=mid) return query(u<<1,l,r);
    else if(l>mid) return query(u<<1|1,l,r);
    else {
        lyw res;
        pushup(res,query(u<<1,l,r),query(u<<1|1,l,r));
        return res;
    }
}

void modify_path(int u,int v,int c)
{
    while(top[u]!=top[v])
    {
        if(dep[top[u]]<dep[top[v]]) swap(u,v);
        modify(1,id[top[u]],id[u],c);
        u=fa[top[u]];
    }
    if(dep[u]<dep[v]) swap(u,v);
    modify(1,id[v],id[u],c);
}

int query_path(int u,int v)
{
    int ans1=-1,ans2=-1,ans=0;
    lyw c;
    while(top[u]!=top[v])
    {
        if(dep[top[u]]<dep[top[v]]) swap(u,v),swap(ans1,ans2);
        c=query(1,id[top[u]],id[u]);
        ans+=c.sum;
        if(ans1==c.rc&&ans1>0) ans++;
        ans1=c.lc;
        u=fa[top[u]];
    }
    if(dep[u]<dep[v]) swap(u,v),swap(ans1,ans2);
    c=query(1,id[v],id[u]);
    ans+=c.sum;
    if(ans1==c.rc&&ans1>0) ans++;
    if(ans2==c.lc&&ans2>0) ans++;
    return ans;
}

signed main()
{
    ios::sync_with_stdio(0);
    cin.tie(0);
    cout.tie(0);
    int T;
    cin>>T;
    while(T--){
        cin>>n>>m;
        memset(h,-1,sizeof h);
        memset(fa,0,sizeof fa);
        memset(son,0,sizeof son);
        idx=0,stp=0;
        for(int i=1;i<n;i++) {
            int u,v;
            cin>>u>>v;
            add(u,v);
            add(v,u);
        }
        
        dfs1(1,1,1);
        dfs2(1,1);
        
        build(1,1,n);
        
        
        for(int i=1;i<=m;i++)
        {
            int op;
            int a,b;
            cin>>op>>a>>b;
            if(op==1)
            {
                modify_path(a,b,i);
            }else
            {
                cout<<query_path(a,b)<<'\n';
            }
        }
    }
}

posted on 2025-04-11 10:35  Liang_9  阅读(29)  评论(0)    收藏  举报