BZOJ_3083_遥远的国度_树链剖分+线段树

BZOJ_3083_遥远的国度_树链剖分

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


总不能每次换根都对整个树剖一遍,因此我们要先以1为根进行树剖,然后对根的位置进行分类讨论。

假设询问的时x,根为root。

当root=x时直接输出整个树的最小值,这个必须要判而不能放到其他分类中。

当root在x的子树外时,由于x的子树没有变,相当于还是查询x的子树。

当root在x的子树内时,答案由两部分组成:

1.x子树外,这个对应树剖序上两段区间。

2.x子树内,需要刨去x到root路径上的点,我们对root不断跳重链,直到跳到x的儿子上,这个点的子树需要刨去,剩下的也是两段区间。

分类讨论完成后直接树剖+线段树即可。

注意权值可能爆int,需要用unsigned int

代码:

#include <stdio.h>
#include <string.h>
#include <algorithm>
using namespace std;
#define N 100050
#define ls p<<1
#define rs p<<1|1
#define inf ((1u<<31)+1)
typedef unsigned int un;
int head[N],to[N<<1],nxt[N<<1],n,cnt,m;
int son[N],fa[N],dep[N],siz[N],top[N],idx[N],root;
un t[N<<2],tag[N<<2],val[N],a[N];
inline void add(int u,int v) {
    to[++cnt]=v; nxt[cnt]=head[u]; head[u]=cnt;
}
void dfs1(int x,int y) {
    int i; fa[x]=y; dep[x]=dep[y]+1; siz[x]=1;
    for(i=head[x];i;i=nxt[i]) {
        if(to[i]!=y) {
            dfs1(to[i],x);
            siz[x]+=siz[to[i]];
            if(siz[to[i]]>siz[son[x]]) son[x]=to[i];
        }
    }
}
void dfs2(int x,int t) {
    top[x]=t; idx[x]=++idx[0];
    if(son[x]) dfs2(son[x],t);
    int i;
    for(i=head[x];i;i=nxt[i]) {
        if(to[i]!=fa[x]&&to[i]!=son[x]) dfs2(to[i],to[i]);
    }
}
void build(int l,int r,int p) {
    if(l==r) {
        t[p]=a[l]; return ;
    }
    int mid=(l+r)>>1;
    build(l,mid,ls); build(mid+1,r,rs);
    t[p]=min(t[ls],t[rs]);
}
inline void pushdown(int p) {
    if(tag[p]) {
        t[ls]=tag[ls]=tag[p];
        t[rs]=tag[rs]=tag[p];
        tag[p]=0;
    }
}
void update(int l,int r,int x,int y,un v,int p) {
    if(x<=l&&y>=r) {
        t[p]=v; tag[p]=v; return ;
    }
    pushdown(p);
    int mid=(l+r)>>1;
    if(x<=mid) update(l,mid,x,y,v,ls);
    if(y>mid) update(mid+1,r,x,y,v,rs);
    t[p]=min(t[ls],t[rs]);
}
un qmin(int l,int r,int x,int y,int p) {
    if(x<=l&&y>=r) return t[p];
    pushdown(p);
    int mid=(l+r)>>1;
    un re=inf;
    if(x<=mid) re=min(re,qmin(l,mid,x,y,ls));
    if(y>mid) re=min(re,qmin(mid+1,r,x,y,rs));
    return re;
}
un solve(int x,int y) {
    un re=inf;
    if(idx[x]<=idx[y]-1) re=min(re,qmin(1,n,idx[x],idx[y]-1,1));
    if(idx[y]+siz[y]<=idx[x]+siz[x]-1) re=min(re,qmin(1,n,idx[y]+siz[y],idx[x]+siz[x]-1,1));
    return re;
}
int main() {
    scanf("%d%d",&n,&m);
    int i,x,y,opt;
    un z;
    for(i=1;i<n;i++) {
        scanf("%d%d",&x,&y);
        add(x,y); add(y,x);
    }
    for(i=1;i<=n;i++) scanf("%u",&i[val]);
    scanf("%d",&root);
    dfs1(1,0);
    dfs2(1,1);
    for(i=1;i<=n;i++) a[idx[i]]=val[i];
    build(1,n,1);
    while(m--) {
        scanf("%d%d",&opt,&x);
        if(opt==1) {
            root=x;
        }else if(opt==2) {
            scanf("%d%u",&y,&z);
            while(top[x]!=top[y]) {
                if(dep[top[x]]>dep[top[y]]) swap(x,y);
                update(1,n,idx[top[y]],idx[y],z,1);
                y=fa[top[y]];
            }
            if(dep[x]<dep[y]) swap(x,y);
            update(1,n,idx[y],idx[x],z,1);
        }else {
            if(root==x) {
                printf("%d
",t[1]);
            }else if(idx[root]<idx[x]||idx[root]>idx[x]+siz[x]-1) {
                printf("%u
",qmin(1,n,idx[x],idx[x]+siz[x]-1,1));
            }else {
                un re=inf;
                if(1<=idx[x]-1) re=min(re,qmin(1,n,1,idx[x]-1,1));
                if(idx[x]+siz[x]<=n) re=min(re,qmin(1,n,idx[x]+siz[x],n,1));
                int y=root;
                if(fa[y]==x) {
                    re=min(re,solve(x,y));
                }else {
                    if(top[y]==top[x]) {
                        re=min(re,solve(x,son[x]));
                    }else {
                        while(fa[y]!=x) {
                            y=fa[y];
                            if(fa[y]==x) break;
                            if(top[x]==top[y]) break;
                            y=top[y];
                        }
                        while(fa[y]!=x) y=fa[y];
                        re=min(re,solve(x,y));
                    }
                }
                printf("%u
",re);
            }
        }
    }
}
原文地址:https://www.cnblogs.com/suika/p/9062604.html