【BZOJ】3123: [Sdoi2013]森林

【算法】可持久化线段树(主席树)+启发式合并+LCA

【题意】给定森林,每次询问u,v路径上的第k小,或给u,v连边(保证森林),n<=8*10^4。

【题解】

区间第k小:离散化,在上一个位置的基础上建可持久化权值线段树,每次比较左子树和k并找到第一个大于等于k的位置。

树上第k小:每个点上其父亲的基础上建树,ans=ask(u)+ask(v)-ask(lca(u,v))-ask(fa[lca(u,v)]),恰好是不重不漏的一条链。

连边:因为保证森林,只要使用启发式合并的方法,对点数少的树DFS重建树即可,启发式合并保证每个点至多重建log n次。

复杂度O(n log2n)。

注意:数据中第一行testcase为数据编号,不是组数。

注意:空间开大!空间复杂度O(n log2n)。

注意:代入主席树用root!

#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cctype>
using namespace std;
const int maxn=100010;
struct tree{int l,r,sum;}t[40000010];
struct edge{int v,from;}e[800010];
int a[maxn],root[maxn],Root[maxn],f[maxn][30],tot,cnt,first[maxn],sz,size[maxn],deep[maxn],p[10],n,m,T,b[maxn];
bool vis[maxn];

int read()
{
    char c;int s=0,t=1;
    while(!isdigit(c=getchar()))if(c=='-')t=-1;
    do{s=s*10+c-'0';}while(isdigit(c=getchar()));
    return s*t;
}
void insert(int u,int v)
{cnt++;e[cnt].v=v;e[cnt].from=first[u];first[u]=cnt;}
void build(int l,int r,int &x,int y,int c){
    x=++sz;
    t[x]=t[y];t[x].sum++;
    if(l==r)return;
    int mid=(l+r)>>1;
    if(c<=mid)build(l,mid,t[x].l,t[y].l,c);
    else build(mid+1,r,t[x].r,t[y].r,c);
}

void dfs(int x,int fa,int rt){
    Root[x]=rt;size[x]=1;vis[x]=1;
    build(1,tot,root[x],root[fa],a[x]);
    for(int j=1;(1<<j)<=deep[x];j++)f[x][j]=f[f[x][j-1]][j-1];
    for(int i=first[x];i;i=e[i].from)if(e[i].v!=fa){
        f[e[i].v][0]=x;
        deep[e[i].v]=deep[x]+1;
        dfs(e[i].v,x,rt);
        size[x]+=size[e[i].v];
    }
}
int lca(int x,int y){
    if(deep[x]<deep[y])swap(x,y);
    int d=deep[x]-deep[y];
    for(int j=0;(1<<j)<=d;j++)if((1<<j)&d)x=f[x][j];//&...
    if(x==y)return x;
    for(int j=20;j>=0;j--)if((1<<j)<=deep[x]&&f[x][j]!=f[y][j]){
        x=f[x][j];y=f[y][j];
    }
    return f[x][0];
}
int ask(int l,int r,int c){
    if(l==r)return l;
    int sum=t[t[p[1]].l].sum+t[t[p[2]].l].sum-t[t[p[3]].l].sum-t[t[p[4]].l].sum;
    int mid=(l+r)>>1;
    if(sum>=c){
        for(int i=1;i<=4;i++)p[i]=t[p[i]].l;
        return ask(l,mid,c);
    }
    else{
        for(int i=1;i<=4;i++)p[i]=t[p[i]].r;
        return ask(mid+1,r,c-sum);
    }
}  
int main(){
    int Test=read();//Test=1
    n=read();m=read();T=read();
    for(int i=1;i<=n;i++)a[i]=read(),b[i]=a[i];
    sort(b+1,b+n+1);tot=n;
    tot=unique(b+1,b+tot+1)-b-1;
    for(int i=1;i<=n;i++)a[i]=lower_bound(b+1,b+tot+1,a[i])-b;
    for(int i=1;i<=m;i++){
        int u=read(),v=read();
        insert(u,v);insert(v,u);
    }
    for(int i=1;i<=n;i++)if(!vis[i])dfs(i,0,i);
    char s[10];
    int lastans=0;
    for(int i=1;i<=T;i++){
        scanf("%s",s);
        if(s[0]=='Q'){
            int u=read()^lastans,v=read()^lastans,w=read()^lastans;
            p[1]=root[u];p[2]=root[v];p[3]=root[lca(u,v)];p[4]=root[f[lca(u,v)][0]];//用root!
            lastans=b[ask(1,tot,w)];
            printf("%d\n",lastans);
        }
        else{
            int u=read()^lastans,v=read()^lastans;
            if(size[Root[u]]<size[Root[v]])swap(u,v);
            size[Root[u]]+=size[Root[v]];insert(u,v);insert(v,u);
            f[v][0]=u;
            deep[v]=deep[u]+1;
            dfs(v,u,Root[u]);
            
        }
    }
    return 0;
}
View Code

 

posted @ 2017-09-03 23:08  ONION_CYC  阅读(243)  评论(0编辑  收藏  举报