【BZOJ3083】遥远的国度 题解

本文详细解析了一道涉及树链剖分的算法题目,重点介绍了链上修改与子树查询的实现方法,以及如何通过巧妙地换根操作来简化问题。文章提供了完整的代码示例,并分析了不同查询情况下的解决方案。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题面:传送门


    链上修改,子树查询,一看就是树链剖分,链修改不需要说了吧,查询 x x 的子树只需要查询[posx,posx+sizex1]即可。

    最难的也是最最关键的地方就是换根操作了。稍稍想一想就知道,不可能是真的换根,所以,我们要将换根操作转化成别的东西。

    画图模拟一下,就会发现在当前根为 root r o o t ,查询的子树的根为 x x 的时候,有3种不同的情况:

    ①若x=root,直接查询整棵树即可。

    ②若在一开始的树中, x x root的祖先,答案为除了包含 root r o o t x x 的子树以外的所有点的最小值。

    ③否则,直接查询原树中x的子树。

    这道题就这样做完了。

    时间复杂度: Θ(nlog22n) Θ ( n l o g 2 2 n )

    全部代码:

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<vector>
using namespace std;
int val[100010];
int pos[100010];
int siz[100010];
int mymin(int x,int y)
{
    if(x==-1)return y;
    if(y==-1)return x;
    return min(x,y);
}
namespace sgt
{
    int mn[300010],root,cnt,son[300010][2],lazy[300010];
    void pushup(int x)
    {
        mn[x]=min(mn[son[x][0]],mn[son[x][1]]);
    }
    void pushdown(int x)
    {
        if(lazy[x])
        {
            mn[x]=lazy[x];
            if(son[x][0] && son[x][1])lazy[son[x][0]]=lazy[son[x][1]]=lazy[x];
            lazy[x]=0;
        }
    }
    void build(int &x,int l,int r)
    {
        x=++cnt;
        if(l==r)
        {
            mn[x]=val[l];
            return;
        }
        int mid=(l+r)>>1;
        build(son[x][0],l,mid);
        build(son[x][1],mid+1,r);
        pushup(x);
    }
    void update(int a,int b,int k,int l,int r,int v)
    {
        pushdown(k);
        if(a>r || b<l)return;
        if(a<=l && b>=r)
        {
            lazy[k]=v;
            pushdown(k);
        }
        else
        {
            int mid=(l+r)>>1;
            update(a,b,son[k][0],l,mid,v);
            update(a,b,son[k][1],mid+1,r,v);
            pushup(k);
        }
    }
    int query(int a,int b,int k,int l,int r)
    {
        pushdown(k);
        if(a>r || b<l)return -1;
        if(a<=l && b>=r)return mn[k];
        int mid=(l+r)>>1;
        int res=mymin(query(a,b,son[k][0],l,mid),query(a,b,son[k][1],mid+1,r));
        pushup(k);
        return res;
    }
}
int n,m;
vector<int>G[100010],son[100010];
int d[100010];
int par[100010][21];
int dep[100010];
int sps[100010];
void dfs(int x,int p)
{
    siz[x]=1;
    for(int i=0;i<G[x].size();i++)
    {
        int y=G[x][i];
        if(y==p)continue;
        son[x].push_back(y);
        par[y][0]=x;
        dep[y]=dep[x]+1;
        dfs(y,x);
        siz[x]+=siz[y];
    }
}
int lca(int x,int y)
{
    if(dep[x]<dep[y])swap(x,y);
    for(int i=20;i>=0;i--)if(dep[par[x][i]]>=dep[y])x=par[x][i];
    if(x==y)return x;
    for(int i=20;i>=0;i--)if(par[x][i]!=par[y][i])x=par[x][i],y=par[y][i];
    return par[x][0];
}
int rt[100010];
int root;
int ind;
void HLD(int x,int lst)
{
    rt[x]=lst;
    pos[x]=++ind;
    val[ind]=d[x];
    if(sps[x])HLD(sps[x],lst);
    for(int i=0;i<son[x].size();i++)if(son[x][i]!=sps[x])HLD(son[x][i],son[x][i]);
}
int gtka(int x,int k)
{
    for(int i=20;i>=0;i--)
    {
        if((1<<i)<=k)
        {
            k-=(1<<i);
            x=par[x][i];
        }
    }
    return x;
}
int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<n;i++)
    {
        int x,y;
        scanf("%d%d",&x,&y);
        G[x].push_back(y);
        G[y].push_back(x);
    }
    for(int i=1;i<=n;i++)scanf("%d",d+i);
    scanf("%d",&root);
    dep[0]=-1;
    dfs(1,0);
    for(int i=1;i<=20;i++)
    {
        for(int j=1;j<=n;j++)par[j][i]=par[par[j][i-1]][i-1];
    }
    for(int i=1;i<=n;i++)
    {
        for(int j=0;j<son[i].size();j++)
        {
            if(!sps[i] || siz[son[i][j]]>siz[sps[i]])sps[i]=son[i][j];
        }
    }
    HLD(1,1);
    sgt::build(sgt::root,1,n);
    int cur=0;
    while(m--)
    {
        int op;
        scanf("%d",&op);
        if(op==1)
        {
            int x;
            scanf("%d",&x);
            root=x;
        }
        else if(op==2)
        {
            int x,y,v;
            scanf("%d%d%d",&x,&y,&v);
            int xy=lca(x,y);
            while(rt[x]!=rt[xy])
            {
                sgt::update(pos[rt[x]],pos[x],sgt::root,1,n,v);
                x=par[rt[x]][0];
            }
            sgt::update(pos[xy],pos[x],sgt::root,1,n,v);
            while(rt[y]!=rt[xy])
            {
                sgt::update(pos[rt[y]],pos[y],sgt::root,1,n,v);
                y=par[rt[y]][0];
            }
            sgt::update(pos[xy],pos[y],sgt::root,1,n,v);
        }
        else
        {
            cur++;
            int x;
            scanf("%d",&x);
            if(x==root)printf("%d\n",sgt::query(1,n,sgt::root,1,n));
            else if(pos[root]>=pos[x] && pos[root]<=pos[x]+siz[x]-1)
            {
                int t=gtka(root,dep[root]-dep[x]-1);
                printf("%d\n",mymin(sgt::query(1,pos[t]-1,sgt::root,1,n),sgt::query(pos[t]+siz[t],n,sgt::root,1,n)));
            }
            else printf("%d\n",sgt::query(pos[x],pos[x]+siz[x]-1,sgt::root,1,n));
        }
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值