• bzoj 3083: 遥远的国度(树上换根操作,树剖+询问整个子树)


    bzoj 3083

    3083: 遥远的国度Time Limit: 10 Sec Memory Limit: 1280 MB

    Description

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

    问题是这样的:遥远的国度有n个城市,这些城市之间由一些路连接且这些城市构成了一颗树。这个国度有一个首都,我们可以把这个首都看做整棵树的根,但遥远的国度比较奇怪,首都是随时有可能变为另外一个城市的。遥远的国度的每个城市有一个防御值,有些时候RapiD会使得某两个城市之间的路径上的所有城市的防御值都变为某个值。RapiD想知道在某个时候,如果把首都看做整棵树的根的话,那么以某个城市为根的子树的所有城市的防御值最小是多少。由于RapiD无法解决这个问题,所以他拦住了zcwwzdjn希望他能帮忙。但zcwwzdjn还要追杀sb的zhx,所以这个重大的问题就被转交到了你的手上。

    Input

    第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为根的子树中的最小防御值。

    Output

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

    Sample Input

    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

    Sample Output

    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。

    题解

    这一题首先肯定要树剖,可是怎样处理询问整个子树的操作呢,其实在树剖过程中得到的 (p[i]),其实对于每个结点 (i) 的子树, (p[i]) 是连续的!

    对于换根操作,当然是不能真的换根,我们可以一直以1为根进行参考,对以1为根的树进行剖分后,以 (i) 为根的子树中最小的 (p[i])(in[i]),最大的 (p[i])(out[i]),假如当前树根为 (cur),询问 (u) 的子树:

    (1)若 (cur==u) ,则是询问整棵树。

    (2)若 (cur) 不属于 (u) 的子树,那么此时以 (cur) 为根的树中 (u) 的子树就是以1为根的树中 (u) 的子树。

    (3)若 (cur) 属于 (u) 的子树,设 (t)(u) 的儿子,且在 (u)(cur) 的路径上

    那么画一画就知道,查询范围应该是 ([1,in[t]-1],[out[t]+1,n]).

    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    #include<cstring>
    #include<vector>
    #include<queue>
    #include<stack>
    using namespace std;
    #define rep(i,a,n) for (int i=a;i<n;i++)
    #define per(i,a,n) for (int i=n-1;i>=a;i--)
    #define pb push_back
    #define fi first
    #define se second
    #define dbg(...) cerr<<"["<<#__VA_ARGS__":"<<(__VA_ARGS__)<<"]"<<endl;
    typedef vector<int> VI;
    typedef long long ll;
    typedef pair<int,int> PII;
    const int inf=0x3fffffff;
    const ll mod=1000000007;
    const int maxn=1e5+10;
    int v[maxn];
    
    int fa[maxn],top[maxn],son[maxn],num[maxn],p[maxn],dep[maxn],fp[maxn];
    //top[v]表示v所在的重链的顶端节点,对于u的轻儿子v有top[v]=v,fa[v]表示v的父亲节点,num[v]表示以v为根的子树的节点数,p[v]表示v与其父亲节点的连边在线段树中的位置,son[v]为v的重儿子,dep为深度
    
    struct node
    {
        int mi,lazy;
    }seg[maxn*4];
    
    void up(int i)
    {
        seg[i].mi=min(seg[i*2].mi,seg[i*2+1].mi);
    }
    void pushdown(int i)
    {
        if(seg[i].lazy)
        {
            seg[i*2].lazy=seg[i*2].mi=seg[i].lazy;
            seg[i*2+1].lazy=seg[i*2+1].mi=seg[i].lazy;
            seg[i].lazy=0;
        }
    }
    void build(int i,int l,int r)
    {
        seg[i].lazy=0;
        if(l==r)
        {
            seg[i].mi=v[fp[l]];
            return;
        }
        int m=(l+r)/2;
        build(i*2,l,m),build(i*2+1,m+1,r);
        up(i);
    }
    
    void update(int i,int l,int r,int L,int R,int v)
    {
        if(l==L&&r==R)
        {
            seg[i].lazy=v;
            seg[i].mi=v;
            return;
        }
        pushdown(i);
        int m=(L+R)/2;
        if(r<=m) update(i*2,l,r,L,m,v);
        else if(l>m) update(i*2+1,l,r,m+1,R,v);
        else
        {
            update(i*2,l,m,L,m,v);
            update(i*2+1,m+1,r,m+1,R,v);
        }
        up(i);
    }
    
    int query(int i,int l,int r,int L,int R)
    {
        if(l==L&&r==R)
        {
            return seg[i].mi;
        }
        if(seg[i].lazy) return seg[i].lazy;
        pushdown(i);
        int m=(L+R)/2;
        if(r<=m) return query(i*2,l,r,L,m);
        else if(l>m) return query(i*2+1,l,r,m+1,R);
        else
        {
            return min(query(i*2,l,m,L,m),query(i*2+1,m+1,r,m+1,R));
        }
    }
    
    int head[maxn];
    struct edge
    {
        int to,next;
    }e[maxn*2];   //
    int tol=0;
    void add(int u,int v)
    {
        e[++tol].to=v,e[tol].next=head[u],head[u]=tol;
    }
    
    int in[maxn],out[maxn];
    int pos;//线段树总区间大小
    void dfs(int u,int f,int d)
    {
        num[u]=1;
        fa[u]=f;
        dep[u]=d;
        for(int i=head[u];i;i=e[i].next)
        {
            int v=e[i].to;
            if(v==f) continue;
            dfs(v,u,d+1);
            if(son[u]==0||num[v]>num[son[u]])
                son[u]=v;
            num[u]+=num[v];
        }
    }
    void dfs2(int u,int sp)
    {
        top[u]=sp;
        if(son[u])
        {
            p[u]=pos;
            fp[pos]=u;
            in[u]=out[u]=pos;
            pos++;
            dfs2(son[u],sp);
            in[u]=min(in[u],in[son[u]]);
            out[u]=max(out[u],out[son[u]]);
        }
        else    //叶子结点
        {
            p[u]=pos;
            fp[pos]=u;
            in[u]=out[u]=pos;
            pos++;
            return;
        }
        for(int i=head[u];i;i=e[i].next)
        {
            int v=e[i].to;
            if(v!=son[u]&&v!=fa[u])
            {
                dfs2(v,v);
                in[u]=min(in[u],in[v]);
                out[u]=max(out[u],out[v]);
            }
        }
    }
    void init()
    {
        pos=1;
        memset(fa,0,sizeof(fa));
        memset(son,0,sizeof(son));
    }
    void change(int u,int v,int n,int w)
    {
        int f1=top[u],f2=top[v];
        while(f1!=f2)
        {
            if(dep[f1]<dep[f2])
            {
                swap(f1,f2);
                swap(u,v);
            }
            update(1,p[f1],p[u],1,n,w);
            u=fa[f1],f1=top[u];
        }
        if(dep[u]>dep[v]) swap(u,v);
        update(1,p[u],p[v],1,n,w);
    }
    
    int f1[maxn][18];
    void bfs(int rt)
    {
        queue<int> q;
        dep[rt] = 1;
        f1[rt][0] = rt;
        q.push(rt);
        while(!q.empty())
        {
            int t = q.front();
            q.pop();
            for(int i = 1 ; i <= 17 ; i++)
                f1[t][i] = f1[f1[t][i-1]][i-1];
            for(int i = head[t] ; i ; i = e[i].next)
            {
                int v = e[i].to;
                if(v == f1[t][0])continue;
                dep[v] = dep[t]+1;
                f1[v][0] = t;
                q.push(v);
            }
        }
    }
    int get_up(int u,int k)
    {
        int tu=u;
        for(int det = k, i = 0; det ;det>>=1, i++)
            if(det&1)
                tu = f1[tu][i];
        return tu;
    }
    
    int main()
    {
        init();
        int n,m;
        scanf("%d%d",&n,&m);
        rep(i,1,n+1) in[i]=1e9,out[i]=0;
        rep(i,1,n)
        {
            int u,v;
            scanf("%d%d",&u,&v);
            add(u,v),add(v,u);
        }
        rep(i,1,n+1) scanf("%d",&v[i]);
        int cur;
        scanf("%d",&cur);
        bfs(1);
        dfs(1,0,1);
        dfs2(1,1);
        build(1,1,n);
        while(m--)
        {
            int op,u,v,w;
            scanf("%d",&op);
            if(op==1)
            {
                scanf("%d",&cur);
            }
            else if(op==2)
            {
                scanf("%d%d%d",&u,&v,&w);
                change(u,v,n,w);
            }
            else if(op==3)
            {
                scanf("%d",&u);
                int ans=1e9;
                if(u==cur)
                    ans=seg[1].mi;
                else if(in[u]<=in[cur]&&out[cur]<=out[u])
                {
                    int k=dep[cur]-dep[u]-1;
                    int tv=get_up(cur,k);
                    if(in[tv]>1)
                    ans=min(ans,query(1,1,in[tv]-1,1,n));
                    if(out[tv]<n)
                    ans=min(ans,query(1,out[tv]+1,n,1,n));
                }
                else
                    ans=query(1,in[u],out[u],1,n);
                printf("%d
    ",ans);
            }
        }
        return 0;
    }
    
  • 相关阅读:
    codevs 1086 栈 2003年NOIP全国联赛普及组
    1200 同余方程 2012年NOIP全国联赛提高组
    【bzoj4939】【YNOI2016】掉进兔子洞(莫队)
    洛谷P3674 小清新人渣的本愿(莫队)
    Lucas卢卡斯定理
    组合数学习笔记
    洛谷P3178 [HAOI2015]树上操作(线段树)
    洛谷P3258 [JLOI2014]松鼠的新家(树上差分+树剖)
    洛谷P2526 [SHOI2001]小狗散步(二分图匹配)
    bzoj3140: [Hnoi2013]消毒(二分图)
  • 原文地址:https://www.cnblogs.com/tarjan/p/7629987.html
Copyright © 2020-2023  润新知