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\n",ans);
        }
    }
    return 0;
}
posted @ 2017-10-05 20:57  tarjan's  阅读(151)  评论(0编辑  收藏  举报