首页 > 代码库 > bzoj 3083 树链剖分

bzoj 3083 树链剖分

  首先我们先将树提出一个根变成有根树,那么我们可以通过树链剖分来实现对于子树的最小值求解,那么按照当前的根和询问的点的相对位置关系我们可以将询问变成某个子树和或者除去某颗子树之后其余的和,前者直接询问区间,后者询问区间的补集。

/**************************************************************
    Problem: 3083
    User: BLADEVIL
    Language: C++
    Result: Accepted
    Time:6412 ms
    Memory:43904 kb
****************************************************************/
 
#include <cstdio>
#include <cstring>
#include <algorithm>
#define maxn 200010
#define maxm 400010
#define inf (~0U>>1)
 
using namespace std;
 
struct segment_tree {
    int left,right,min_,lazy;
}t[maxn<<2];
 
int n,m,l,rot,tot;
int key[maxn];
int other[maxm],pre[maxm],last[maxn];
int size[maxn],max_son[maxn],top[maxn],dep[maxn],a[maxn],num[maxn][2],jump[maxn][20],dis[maxn];
 
void connect(int x,int y) {
    pre[++l]=last[x];
    last[x]=l;
    other[l]=y;
}
 
void update(int x) {
    t[x].min_=min(t[x<<1].min_,t[(x<<1)+1].min_);
}
 
void push_down(int x) {
    t[x<<1].lazy=t[(x<<1)+1].lazy=t[x].lazy;
    t[x<<1].min_=t[(x<<1)+1].min_=t[x].min_;
    t[x].lazy=0;
}
 
int get_lca(int x,int y) {
    if (dis[x]>dis[y]) swap(x,y);
    int det=dis[y]-dis[x];
    for (int j=0;j<=18;j++)  if (det&(1<<j)) y=jump[y][j];
    if (x==y) return x;
    for (int j=18;j>=0;j--)
        if (jump[x][j]!=jump[y][j]) x=jump[x][j],y=jump[y][j];
    return jump[x][1];
}
 
int get_lca_son(int x,int y) {
    if (dis[x]>dis[y]) swap(x,y);
    for (int j=18;j>=0;j--)
        if (dis[jump[y][j]]>dis[x]) y=jump[y][j];
    return y;
}
 
void dfs(int x) {
    size[x]=1;
    for (int p=last[x];p;p=pre[p]) {
        if (dis[other[p]]) continue;
        jump[other[p]][0]=x; dis[other[p]]=dis[x]+1;
        dfs(other[p]);
        size[x]+=size[other[p]];
        if (size[other[p]]>size[max_son[x]]) max_son[x]=other[p];
    }
}
 
void make_segment(int x) {
    num[x][0]=++tot; a[tot]=key[x];
    if (max_son[x]) {
        top[max_son[x]]=top[x];
        dep[max_son[x]]=dep[x];
        make_segment(max_son[x]);
    }
    for (int p=last[x];p;p=pre[p]) {
        if (other[p]==jump[x][0]) continue;
        if (other[p]==max_son[x]) continue;
        top[other[p]]=other[p];
        dep[other[p]]=dep[x]+1;
        make_segment(other[p]);
    }
    num[x][1]=tot;
}
 
void build_segment(int x,int l,int r) {
    t[x].left=l; t[x].right=r;
    if (t[x].left==t[x].right) {
        t[x].min_=a[t[x].left];
        return ;
    }
    int mid=t[x].left+t[x].right>>1;
    build_segment(x<<1,l,mid); build_segment((x<<1)+1,mid+1,r);
    update(x);
}
 
void change_segment_tree(int x,int l,int r,int y) {
    if (t[x].lazy) push_down(x);
    if ((t[x].left==l)&&(t[x].right==r)) {
        t[x].lazy=t[x].min_=y;
        return ;
    }
    int mid=t[x].left+t[x].right>>1;
    if (l>mid) change_segment_tree((x<<1)+1,l,r,y); else
    if (r<=mid) change_segment_tree(x<<1,l,r,y); else
        change_segment_tree(x<<1,l,mid,y),change_segment_tree((x<<1)+1,mid+1,r,y);
    update(x);
}
 
void change(int x,int y,int z) {

    if (dep[x]>dep[y]) swap(x,y);
    while (dep[x]!=dep[y]) {
        change_segment_tree(1,num[top[y]][0],num[y][0],z);
        y=jump[top[y]][0];
    }
    while (top[x]!=top[y]) {
        change_segment_tree(1,num[top[x]][0],num[x][0],z);
        change_segment_tree(1,num[top[y]][0],num[y][0],z);
        x=jump[top[x]][0];
        y=jump[top[y]][0];
    }
    if (num[x][0]>num[y][0]) swap(x,y);
    change_segment_tree(1,num[x][0],num[y][0],z);
}
 
int query_segment_tree(int x,int l,int r) {
    if (l>r) return inf;
    if (t[x].lazy) push_down(x);
    if ((t[x].left==l)&&(t[x].right==r)) return t[x].min_;
    int mid=t[x].left+t[x].right>>1;
    if (l>mid) return query_segment_tree((x<<1)+1,l,r); else
    if (r<=mid) return query_segment_tree(x<<1,l,r); else
        return min(query_segment_tree(x<<1,l,mid),query_segment_tree((x<<1)+1,mid+1,r));
}
 
void query(int x) {
    if (x==rot) {
        printf("%d\n",t[1].min_);
        return ;
    }
    if (get_lca(x,rot)!=x) printf("%d\n",query_segment_tree(1,num[x][0],num[x][1])); else {
        int y=get_lca_son(x,rot);
        printf("%d\n",min(query_segment_tree(1,1,num[y][0]-1),query_segment_tree(1,num[y][1]+1,n)));
        //printf("%d %d\n",query_segment_tree(1,1,num[y][0]-1),query_segment_tree(1,num[y][1]+1,n));
        //printf("%d\n",y);
        //printf("%d %d\n",num[y][0],num[y][1]);
    }
}
 
int main() {
    //freopen("country.in","r",stdin); freopen("country.out","w",stdout);
    scanf("%d%d",&n,&m);
    for (int i=1;i<n;i++) {
        int x,y; scanf("%d%d",&x,&y);
        connect(x,y); connect(y,x);
    }
    for (int i=1;i<=n;i++) scanf("%d",&key[i]);
    scanf("%d",&rot);
    dis[rot]=1;
    dfs(rot);
    top[rot]=rot; dep[rot]=1;
    make_segment(rot);
    //for (int i=1;i<=n;i++) printf("%d %d %d %d %d\n",i,max_son[i],size[i],num[i][0],num[i][1]);
    build_segment(1,1,n);
    for (int j=1;j<=18;j++)
        for (int i=1;i<=n;i++) jump[i][j]=jump[jump[i][j-1]][j-1];
    while (m--) {
        int opt; scanf("%d",&opt);
        if (opt==1) scanf("%d",&rot); else
        if (opt==2) {
            int l,r,y; scanf("%d%d%d",&l,&r,&y);
            change(l,r,y);
        } else {
            int x; scanf("%d",&x);
            query(x);
        }
    }
    return 0;
}