zoukankan      html  css  js  c++  java
  • P3979 遥远的国度

    题目描述

    zcwwzdjn在追杀十分sb的zhx,而zhx逃入了一个遥远的国度。当zcwwzdjn准备进入遥远的国度继续追杀时,守护神RapiD阻拦了zcwwzdjn的去路,他需要zcwwzdjn完成任务后才能进入遥远的国度继续追杀。

    问题是这样的:遥远的国度有n个城市,这些城市之间由一些路连接且这些城市构成了一颗树。这个国度有一个首都,我们可以把这个首都看做整棵树的根,但遥远的国度比较奇怪,首都是随时有可能变为另外一个城市的。遥远的国度的每个城市有一个防御值,有些时候RapiD会使得某两个城市之间的路径上的所有城市的防御值都变为某个值。

    RapiD想知道在某个时候,如果把首都看做整棵树的根的话,那么以某个城市为根的子树的所有城市的防御值最小是多少。

    由于RapiD无法解决这个问题,所以他拦住了zcwwzdjn希望他能帮忙。但zcwwzdjn还要追杀sb的zhx,所以这个重大的问题就被转交到了你的手上。

    输入输出格式

    输入格式:

    第1行两个整数n m,代表城市个数和操作数。

    第2行至第n行,每行两个整数 u v,代表城市u和城市v之间有一条路。

    第n+1行,有n个整数,代表所有点的初始防御值。

    第n+2行一个整数 id,代表初始的首都为id。

    第n+3行至第n+m+2行,首先有一个整数opt,如果opt=1,接下来有一个整数id,代表把首都修改为id;如果opt=2,接下来有三个整数p1 p2 v,代表将p1 p2路径上的所有城市的防御值修改为v;如果opt=3,接下来有一个整数 id,代表询问以城市id为根的子树中的最小防御值。

    输出格式:

    对于每个opt=3的操作,输出一行代表对应子树的最小点权值。

    输入输出样例

    输入样例#1: 复制
    3 7
    1 2
    1 3
    1 2 3
    1
    3 1
    2 1 1 6
    3 1
    2 2 2 5
    3 1
    2 3 3 4
    3 1
    输出样例#1: 复制
    1
    2
    3
    4

    说明

    对于20%的数据,n<=1000 m<=1000。

    对于另外10%的数据,n<=100000,m<=100000,保证修改为单点修改。

    对于另外10%的数据,n<=100000,m<=100000,保证树为一条链。

    对于另外10%的数据,n<=100000,m<=100000,没有修改首都的操作。

    对于100%的数据,n<=100000,m<=100000,0<所有权值<=2^31。

    By Zhonghaoxi

    // luogu-judger-enable-o2
    //这道题给它打普及+的标签实在是亏了这道题
    //除了换根之外,别的操作好像就是树剖的基本操作了
    //换根怎么搞呢?不能换过来然后重新求dfs序然后建树吧
    //画一下图,可以看出来,换根之后树的形态是会改变的,在新树根上边的点,他们的爸爸兄弟变成了自己的儿子
    //但是,新树根的儿子们的形态是没有改变的,和新树根不在同一条链上的点的子树也是没有变的
    //变了的只是新树根到一开始的树根那条链上的点
    //所以,如果要查询的点是在这条链上,我们怎么做呢?
    //可以发现,要查询的点的爸爸变成了和他用属于这条链上的那个儿子,
    //所以这个点所掌管的子树就是整棵树挖去了这个儿子的子树 
    //那么它的新儿子们在线段树对应的区间是哪一块呢?
    //就是1->fa_s-1  fa_t+1->n, 
    //所以我们求出它的这条链上的儿子,然后query上边的那两个区间就行了。
    //但是怎么找这个儿子呢?我们知道它一定在这条链上,那么树剖处理的top是不能找到这个儿子的
    //那我们要沿着now_root的father一个一个向上跳?
    //不可能的   我们可以在处理一个倍增数组,用这个倍增数组来找儿子。
    //然后问题就完美的解决了。 
    
    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    using namespace std;
    
    const int N=1e5+5;
    const int INF=2147483647;
    
    int n,m,id;
    int opt,p1,p2,v,now_root;
    int w[N];
    int fa[N][17];
    int head[N],num_edge;
    struct Edge
    {
        int v,nxt;
    }edge[N<<1];
    struct Node
    {
        int fa,son;
        int dep;
        int top;
        int size;
        int s,t;
    }node[N];
    struct TREE
    {
        TREE *lson,*rson;
        int l,r,mid;
        int minn,lazy;
    }tree[N<<2];
    
    typedef TREE* Tree;
    Tree Root,now_node=tree;
    
    inline int read()
    {
        char c=getchar();int num=0;
        for(;!isdigit(c);c=getchar());
        for(;isdigit(c);c=getchar())
            num=num*10+c-'0';
        return num;
    }
    
    inline void add_edge(int u,int v)
    {
        edge[++num_edge].v=v;
        edge[num_edge].nxt=head[u];
        head[u]=num_edge;
    }
    
    void dfs1(int u)
    {
        for(int i=1;i<=17;++i)    //处理倍增数组 
        {
            fa[u][i]=fa[fa[u][i-1]][i-1];
            if(!fa[u][i])
                break;
        }
        node[u].size=1;
        for(int i=head[u],v;i;i=edge[i].nxt)
        {
            v=edge[i].v;
            if(v==node[u].fa)
                continue;
            node[v].fa=u;
            node[v].dep=node[u].dep+1;
            fa[v][0]=u;        //爸爸 
            dfs1(v);
            node[u].size+=node[v].size;
            if(node[v].size>node[node[u].son].size)
                node[u].son=v;        //重儿子 
        }
    }
    
    int bound;
    void dfs2(int u,int top)
    {
        node[u].top=top;
        node[u].s=++bound;        //线段树对应区间的左端点 
        if(node[u].son)
        {
            dfs2(node[u].son,top);
            for(int i=head[u],v;i;i=edge[i].nxt)
            {
                v=edge[i].v;
                if(v==node[u].fa||v==node[u].son)
                    continue;
                dfs2(v,v);
            }
        }
        node[u].t=bound;    //线段树对应区间的右端点 
    }
    
    void build(Tree &root,int l,int r)
    {
        root=++now_node;
        root->l=l,root->r=r,root->mid=l+r>>1;
        root->lazy=-1;
        if(l==r)
            return;
        build(root->lson,l,root->mid);
        build(root->rson,root->mid+1,r);
    }
    
    inline void pushdown(Tree root)
    {
        if(root->lazy!=-1)
        {
            root->lson->lazy=root->lazy;
            root->rson->lazy=root->lazy;
            root->lson->minn=root->lazy;
            root->rson->minn=root->lazy;
            root->lazy=-1;
        }
    }
    
    void update(Tree root,int l,int r,int val)        //线段树修改 
    {
        if(l<=root->l&&root->r<=r)
        {
            root->minn=val;
            root->lazy=val;
            return;
        }
        pushdown(root);
        if(r<=root->mid)
            update(root->lson,l,r,val);
        else if(l>root->mid)
            update(root->rson,l,r,val);
        else
        {
            update(root->lson,l,root->mid,val);
            update(root->rson,root->mid+1,r,val);
        }
        root->minn=min(root->lson->minn,root->rson->minn);
    }
    
    int query(Tree root,int l,int r)    //线段树查询 
    {
        if(l<=root->l&&root->r<=r)
            return root->minn;
        pushdown(root);
        if(r<=root->mid)
            return query(root->lson,l,r);
        else if(l>root->mid)
            return query(root->rson,l,r);
        else
            return min(query(root->lson,l,root->mid),query(root->rson,root->mid+1,r));
    }
    
    inline void Modify(int x,int y,int val)        //树剖的modify操作 
    {
        int fx=node[x].top,fy=node[y].top;
        while(fx!=fy)
        {
            if(node[fx].dep>node[fy].dep)
            {
                update(Root,node[fx].s,node[x].s,val);
                x=node[fx].fa;
                fx=node[x].top;
            }
            else
            {
                update(Root,node[fy].s,node[y].s,val);
                y=node[fy].fa;
                fy=node[y].top;
            }
        }
        if(node[x].dep>node[y].dep)
            update(Root,node[y].s,node[x].s,val);
        else
            update(Root,node[x].s,node[y].s,val);
    }
    
    inline bool judge(int Y)    //判断一下在不在一条链上 
    {
        int x=now_root,y=Y;
        if(node[x].dep<node[y].dep)
            swap(x,y);
        int cha=node[x].dep-node[y].dep;
        for(int i=0;i<=17;++i)
        {
            if(cha&(1<<i))
                x=fa[x][i];
        }
        if(x!=y)
        {
            for(int i=17;i>=0;--i)
            {
                if(fa[x][i]!=fa[y][i])
                    x=fa[x][i],y=fa[y][i];
            }
            x=fa[x][0];
        }
        if(x==Y)
            return 1;
        return 0;
    }
    
    inline int jump(int x,int dep)        //倍增往上跳,找儿子 
    {
        for(int i=0;i<=17;++i)
            if(dep&(1<<i))
                x=fa[x][i];
        return x;
    }
    
    int main()
    {
        n=read(),m=read();
        int u,v;
        for(int i=1;i<n;++i)
        {
            u=read(),v=read();
            add_edge(u,v);
            add_edge(v,u);
        }
        for(int i=1;i<=n;++i)
            w[i]=read();
        id=read(),now_root=id;
        dfs1(id);
        dfs2(id,id);
        build(Root,1,n);
        for(int i=1;i<=n;++i)
            update(Root,node[i].s,node[i].s,w[i]);
        for(int i=1;i<=m;++i)
        {
            opt=read();
               if(opt==1)
            {
                id=read();
                now_root=id;
            }
            else if(opt==2)
            {
                p1=read(),p2=read(),v=read();
                Modify(p1,p2,v);
            }
            else
            {
                p1=read();
                if(p1==now_root)    //当前点是树根,查询整棵树 
                    printf("%d
    ",query(Root,1,n));
                else if(node[p1].s>=node[now_root].s&&node[p1].s<=node[now_root].t)        //当前点是当前树根的儿子或者等于当前树根 
                    printf("%d
    ",query(Root,node[p1].s,node[p1].t));
                else if(judge(p1))        //当前点和树根在一条链上 
                {
    //                int minn=INF;
    //                minn=min(minn,query(Root,1,node[now_root].s-1));
    //                if(node[now_root].t<n)
    //                    minn=min(minn,query(Root,node[now_root].t+1,n));
    //                printf("%d
    ",minn);
                    int dep=node[now_root].dep-node[p1].dep-1;
                    int tmp=jump(now_root,dep);
                    int minn=INF;
                    if(node[tmp].s>1)
                        minn=min(minn,query(Root,1,node[tmp].s-1));
                    if(node[tmp].t<n)
                        minn=min(minn,query(Root,node[tmp].t+1,n));
                    printf("%d
    ",minn);
                }
                else    //不在一条链上,没影响 
                    printf("%d
    ",query(Root,node[p1].s,node[p1].t));
            }
        }
        return 0;
    }
    /*
    8 100
    1 2
    1 3
    2 4
    2 5
    5 8
    3 6
    6 7
    7 5 3 2 4 1 8 6
    1
    1 3
    3 1
    */
  • 相关阅读:
    UVALive 5983 MAGRID DP
    2015暑假训练(UVALive 5983
    poj 1426 Find The Multiple (BFS)
    poj 3126 Prime Path (BFS)
    poj 2251 Dungeon Master 3维bfs(水水)
    poj 3278 catch that cow BFS(基础水)
    poj3083 Children of the Candy Corn BFS&&DFS
    BZOJ1878: [SDOI2009]HH的项链 (离线查询+树状数组)
    洛谷P3178 [HAOI2015]树上操作(dfs序+线段树)
    洛谷P3065 [USACO12DEC]第一!First!(Trie树+拓扑排序)
  • 原文地址:https://www.cnblogs.com/lovewhy/p/8544593.html
Copyright © 2011-2022 走看看