bzoj 3306: 树

Description

给定一棵大小为 n 的有根点权树,支持以下操作:

  • 换根

  • 修改点权

  • 查询子树最小值

Input

第一行两个整数 n,Q ,分别表示树的大小和操作数。

接下来 n 行,每行两个整数 f,v ,第 i+1 行的两个数表示点 i 的父亲和点 i 的权。保证 f<i 。如果 f=0 ,那么 i 为根。输入数据保证只有i=1时, f=0

接下来 m 行,为以下格式中的一种:

  • Vxy 表示把点 x 的权改为 y

    • Ex 表示把有根树的根改为点 x

    • Qx 表示查询点 x 的子树最小值

    Output

    对于每个 Q ,输出子树最小值。

    Sample Input

    3 7
    0 1
    1 2
    1 3
    Q 1
    V 1 6
    Q 1
    V 2 5
    Q 1
    V 3 4
    Q 1

    Sample Output

    1
    2
    3
    4

    HINT

    对于 100% 的数据: n,Q105


    solution

    • 这道题先把整个树dfs一下,搞出dfs序来,那修改点权和查询最小值就是裸的线段树了

    • 就是换根看上去有点难,来考虑一下根对查询操作的影响

    • 假设我们要查的那个节点是 x ,根为 root

    • if(x=root) 显然这个时候就是查询整棵树的最小值

    • if(xroot) ,这个时候又要分两种情况来考虑

    • 如果我们把根换到不是 x 的子树的点,那么这个时候对 x 是没什么影响

    qwq

    • 如我我们把根从 root 换到 a ,并不会影响到 x 的子树,所以这个时候和平常一样query就好

    • 如果我们把根换到属于 x 的子树的点

    • 一开始如果树长这个样

    qwq

    • 我们把根换到属于 x 的子树的点 a ,那树就成了这个样子

    qwq

    • 对比一下上面那个图,我们发现换根之后要查询的范围就是不换根之前所有的店除了 a a <script type="math/tex" id="MathJax-Element-3143">a</script> 的子树

    • 把这个推广一下就可以了

    code

    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    using namespace std;
    typedef long long ll;
    
    template<typename T>
    void input(T &x) {
        x=0; T a=1;
        register char c=getchar();
        for(;c<'0'||c>'9';c=getchar())
            if(c=='-') a=-1;
        for(;c>='0'&&c<='9';c=getchar())
            x=x*10+c-'0';
        x*=a;
        return;
    }
    
    #define MAXN 100010
    
    struct Edge {
        int u,v,next;
        Edge(int u=0,int v=0,int next=0):
            u(u),v(v),next(next) {}
    };
    
    Edge edge[MAXN];
    int head[MAXN],cnt=0;
    
    void addedge(int u,int v) {
        edge[++cnt]=Edge(u,v,head[u]);
        head[u]=cnt;
        return;
    }
    
    int dfn[MAXN],last[MAXN],order[MAXN],timee;
    
    int fa[MAXN][21],depth[MAXN];
    
    void dfs(int u) {
        order[++timee]=u;
        dfn[u]=timee;
        for(int i=head[u];i;i=edge[i].next)
            if(edge[i].v!=fa[u][0]) {
                depth[edge[i].v]=depth[u]+1;
                dfs(edge[i].v);
            }
        last[u]=timee;
        return;
    }
    
    int w[MAXN];
    
    struct Segment_Tree {
        int l,r,Min;
    };
    
    Segment_Tree t[MAXN<<2];
    
    void updata(int now) {
        t[now].Min=min(t[now<<1].Min,t[now<<1|1].Min);
        return;
    }
    
    void build(int now,int l,int r) {
        t[now].l=l,t[now].r=r;
        if(l==r) {
            t[now].Min=w[order[l]];
            return;
        }
        int mid=l+r>>1;
        build(now<<1,l,mid);
        build(now<<1|1,mid+1,r);
        updata(now);
        return;
    }
    
    void Modify(int now,int pos,int x) {
        int l=t[now].l,r=t[now].r;
        if(l==r) {
            t[now].Min=x;
            return;
        }
        int mid=l+r>>1;
        if(pos<=mid) Modify(now<<1,pos,x);
        else Modify(now<<1|1,pos,x);
        updata(now);
        return;
    }
    
    #define inf 2147483647
    
    int query(int now,int L,int R) {
        if(L>R) return inf;
        int l=t[now].l,r=t[now].r;
        if(l==L&&R==r) return t[now].Min;
        int mid=l+r>>1;
        if(R<=mid) return query(now<<1,L,R);
        else if(L>mid) return query(now<<1|1,L,R);
        else {
            int a=query(now<<1,L,mid),
                b=query(now<<1|1,mid+1,R);
            return min(a,b);
        }
    }
    
    int up(int x,int depth) {
        for(int i=19;i>=0;i--)
            if(depth&(1<<i)) x=fa[x][i];
        return x;
    }
    
    int main() {
        int n,T,root=1;
        input(n),input(T);
        input(fa[1][0]),input(w[1]);
        for(int i=2;i<=n;i++) {
            input(fa[i][0]),input(w[i]);
            addedge(fa[i][0],i);
        }
        dfs(1);
        for(int j=1;j<=19;j++)
            for(int i=1;i<=n;i++)
                if(fa[i][j-1])
                    fa[i][j]=fa[fa[i][j-1]][j-1];
        build(1,1,n);
        char op[3];
        int x,y;
        while(T--) {
            scanf("%s",op);
            input(x);
            if(op[0]=='V') {
                input(y);
                Modify(1,dfn[x],y);
            } else if(op[0]=='E') root=x;
            else if(root==x) printf("%d\n",query(1,1,n));
            else if(dfn[x]<=dfn[root]&&last[root]<=last[x]) {
                y=up(root,depth[root]-depth[x]-1);
                printf("%d\n",min(query(1,1,dfn[y]-1),query(1,last[y]+1,n)));
            } else printf("%d\n",query(1,dfn[x],last[x])); 
        }
        return 0;
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值