BZOJ3123: [Sdoi2013]森林

题目:http://www.lydsy.com/JudgeOnline/problem.php?id=3123

主席树+启发式合并。

首先对于每棵树建主席树,然后合并的时候就把点数小的暴力插进点数大的那棵树里面。

然后查询的话就在主席树上二分就可以了。

注意点:在暴力重建的过程中边是确确实实要连的否则会丢失信息。。

调了很久TAT。。

#include<cstring>
#include<iostream>
#include<algorithm>
#include<cstdio>
#define rep(i,l,r) for (int i=l;i<=r;i++)
#define down(i,l,r) for (int i=l;i>=r;i--)
#define clr(x,y) memset(x,y,sizeof(x))
#define inf 1000000009
#define ll long long
using namespace std;
struct data{int obj,pre;
}e[165000];
int sum[24005000],root[80050],ls[24005000],rs[24005000];
int head[80050],dep[80050],fa[80050][21],a[80050],b[80050],s[80050];
int bin[21];
int n,m,q,ans,tot,cnt,tt,num;
char ch[2];
int read(){
    int x=0,f=1; char ch=getchar();
    while (!isdigit(ch)){if (ch=='-') f=-1; ch=getchar();}
    while (isdigit(ch)){x=x*10+ch-'0'; ch=getchar();}
    return x*f;
}
void insert(int x,int y){
    e[++tot].obj=y; e[tot].pre=head[x]; head[x]=tot;
}
void add(int l,int r,int x,int &y,int val){
    y=++cnt;
    sum[y]=sum[x]+1;
    if (l==r) return;
    int mid=(l+r)/2;
    if (val<=mid) rs[y]=rs[x],add(l,mid,ls[x],ls[y],val);
    if (val>mid) ls[y]=ls[x],add(mid+1,r,rs[x],rs[y],val);
}
void dfs(int u){
    s[u]=1;
    add(1,num,root[fa[u][0]],root[u],a[u]);
    rep(i,1,20) if (dep[u]>=bin[i]) fa[u][i]=fa[fa[u][i-1]][i-1];    
    for (int j=head[u];j;j=e[j].pre){
        int v=e[j].obj;
        if (v!=fa[u][0]){
            fa[v][0]=u; dep[v]=dep[u]+1;
            dfs(v);
            s[u]+=s[v];
        }
    }
}
int lca(int x,int y){
    if (dep[x]<dep[y]) swap(x,y);
    int t=dep[x]-dep[y];
    rep(i,0,20) if (t&bin[i]) x=fa[x][i];
    down(i,20,0) if (fa[x][i]!=fa[y][i]) x=fa[x][i],y=fa[y][i];
    if (x==y) return x;
    return fa[x][0]; 
}
void cl(int u,int f){
    clr(fa[u],0); dep[u]=0;
    for (int j=head[u];j;j=e[j].pre){
        int v=e[j].obj;
        if (v!=f) cl(v,u);
    }
}
void link(int x,int y){
    cl(y,0);
    dep[y]=dep[x]+1; fa[y][0]=x; insert(x,y); insert(y,x);
    dfs(y);
}
void rebuild(int x,int y){
    int sx=x,sy=y;
    int t=dep[x]; rep(i,0,20) if (t&bin[i]) x=fa[x][i];
    t=dep[y]; rep(i,0,20) if (t&bin[i]) y=fa[y][i];
    if (s[x]<s[y]) swap(x,y),swap(sx,sy);
    s[x]+=s[y]; link(sx,sy);    
}
int solve(int x,int y,int k){
    int z=lca(x,y),t=fa[z][0],a=root[x],b=root[y],c=root[z],d=root[t];
    int l=1,r=num,now=0;
    while (l<r){
        int mid=(l+r)/2;
        now=sum[ls[a]]+sum[ls[b]]-sum[ls[c]]-sum[ls[d]];
        if (now<k) {
            k-=now; 
            a=rs[a],b=rs[b],c=rs[c],d=rs[d];
            l=mid+1;
        }
        else {
            a=ls[a],b=ls[b],c=ls[c],d=ls[d];
            r=mid;
        }
    }
    return l;
}
int Find(int x){
    int l=1,r=num,mid;
    while (l<r){
        mid=(l+r)/2;
        if (b[mid]==x) return mid;
        if (b[mid]<x) l=mid+1;
        else r=mid-1;
    }
    return l;
}
int main(){
    tt=read();
    bin[0]=1; rep(i,1,20) bin[i]=bin[i-1]*2; 
    ans=0;
        n=read(); m=read(); q=read(); 
        rep(i,1,n) a[i]=read(),b[i]=a[i];
        sort(b+1,b+1+n); num=unique(b+1,b+1+n)-b-1;
        rep(i,1,n) a[i]=Find(a[i]);
        rep(j,1,m){
            int x=read(),y=read();
            insert(x,y); insert(y,x);
        }
        rep(i,1,n) if (!root[i]) dfs(i);
        rep(i,1,q){
            scanf("%s",ch);
            if (ch[0]=='Q'){
                int x=read()^ans,y=read()^ans,k=read()^ans;
                ans=b[solve(x,y,k)];
                printf("%d\n",ans);
            }
            else {
                int x=read()^ans,y=read()^ans;
                rebuild(x,y);
            }
        }
    return 0;
}

 

posted on 2016-01-21 18:00  ctlchild  阅读(178)  评论(0编辑  收藏  举报

导航