bzoj3083: 遥远的国度

树链剖分。

除去换根的俩个操作,在树链剖分后很容易实现。

换根以后只需操作的时候进行分类讨论即可。

操作2不会受到换根的影响。

操作3:如果询问点就是根,输出整个线段树最小值。

          如果询问点在根到1的路径上,查询(1,st[p])和(ed[p]+1,n)的最小值,st和ed分别为出入栈的编号,p为路径上最靠近询问点的点。

         其他情况下,输出范围(st[u],ed[u])的最小值,u为询问点。

细节方面要注意的是,一定要求p,因为ed[p+1]不一定等于ed[u],u为询问点。

#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
const int maxn = 200000 + 10;
const int maxm= 1600000 + 10;
const int inf = 0x7fffffff;
const int maxh = 25;

int g[maxn],v[maxm],next[maxm],eid;
int root[maxm],st[maxm],ed[maxm];
int dep[maxn],anc[maxn][maxh],size[maxn],son[maxn];
int n,m,vid,cap,op,x,y,val;

void addedge(int a,int b) {
    v[eid]=b; next[eid]=g[a]; g[a]=eid++;
    v[eid]=a; next[eid]=g[b]; g[b]=eid++;
}

struct Segtree {
    #define lc(x) ((x)<<1)
    #define rc(x) (((x)<<1)|1)
    
    int l[maxm],r[maxm];
    int minv[maxm],sam[maxm];
    
    inline void update(int x) {
        minv[x]=min(minv[lc(x)],minv[rc(x)]);
    }
    
    void push(int x) {
        if(!sam[x]) return;
        minv[lc(x)]=sam[lc(x)]=sam[x];
        minv[rc(x)]=sam[rc(x)]=sam[x];
        sam[x]=0;
    }
    
    void change(int x,int L,int R,int val) {
        if(L>r[x] || R<l[x]) return;
        if(L<=l[x] && r[x]<=R) {
            minv[x]=sam[x]=val;
            return;
        }
        push(x);
        change(lc(x),L,R,val);
        change(rc(x),L,R,val);
        update(x);
    }
    
    int query(int x,int L,int R) {
        if(L>r[x] || R<l[x]) return inf;
        if(L<=l[x] && r[x]<=R) return minv[x];
        push(x);
        return min(query(lc(x),L,R),query(rc(x),L,R));
    }
    
    void build(int x,int L,int R) {
        l[x]=L; r[x]=R;
        if(L==R) {
            minv[x]=sam[x]=0;
            return;
        }
        int mid=(L+R)>>1;
        build(lc(x),L,mid);
        build(rc(x),mid+1,R);
    }
}seg;

void dfs1(int u) {
    dep[u]=dep[anc[u][0]]+1;
    size[u]=1;
    for(int h=1;h<maxh;h++) anc[u][h]=anc[anc[u][h-1]][h-1];
    
    for(int i=g[u];~i;i=next[i]) if(v[i]!=anc[u][0]) {
        anc[v[i]][0]=u;
        dfs1(v[i]);
        size[u]+=size[v[i]];
        if(size[v[i]]>size[son[u]]) son[u]=v[i];    
    }
}

void dfs2(int u,int r) {
    root[u]=r; st[u]=++vid; //ID[vid]=u;
    if(son[u]) dfs2(son[u],r);
    for(int i=g[u];~i;i=next[i]) if(v[i]!=anc[u][0] && v[i] != son[u]) 
        dfs2(v[i],v[i]);
    ed[u]=vid;
}

int LCA(int a,int b) {
    if(dep[a]<dep[b]) swap(a,b);
    for(int h=maxh-1;h>=0;h--) if(dep[anc[a][h]]>=dep[b]) a=anc[a][h];
    if(a==b) return a;
    for(int h=maxh-1;h>=0;h--) 
        if(anc[a][h]!=anc[b][h]) {
            a=anc[a][h];
            b=anc[b][h];
        }
    return anc[a][0];
}

void alter(int x,int y,int v) {
    while(root[y]!=root[x]) {
        seg.change(1,st[root[y]],st[y],v);
        y=anc[root[y]][0];    
    }
    seg.change(1,st[x],st[y],v);
}

int ANC(int x,int k) {
    for(int h=0;h<maxh && k;h++,k>>=1) if(k&1) x=anc[x][h];
    return x;
}

void query(int id) {
    if(cap==id) printf("%d\n",seg.query(1,1,n));
    else if(LCA(cap,id)==id) {
        int p=ANC(cap,dep[cap]-dep[id]-1);
        printf("%d\n",min(seg.query(1,1,st[p]-1),seg.query(1,ed[p]+1,n)));
    }
    else printf("%d\n",seg.query(1,st[id],ed[id]));
}

int main() {
    memset(g,-1,sizeof(g));
    scanf("%d%d",&n,&m);
    for(int i=1,a,b;i<n;i++) {
        scanf("%d%d",&a,&b);
        addedge(a,b);
    }
    anc[1][0]=1; cap=1;
    seg.build(1,1,n);
    dfs1(1);
    dfs2(1,1); 
    for(int i=1,a;i<=n;i++) {
        scanf("%d",&a);
        seg.change(1,st[i],st[i],a);    
    }
    scanf("%d",&cap);
    while(m--) {
        scanf("%d%d",&op,&x);
        if(op==1) cap=x;
        else if(op==2) {
            scanf("%d%d",&y,&val);
            int lca=LCA(x,y);
            alter(lca,x,val);
            alter(lca,y,val);
        }
        else query(x);
    }
    return 0;
}
posted @ 2016-06-29 17:33  invoid  阅读(194)  评论(0编辑  收藏  举报