P3302 [SDOI2013]森林(主席树+启发式合并)

P3302 [SDOI2013]森林

主席树+启发式合并

(我以前的主席树板子是错的.......坑了我老久TAT)

第k小问题显然是主席树。

我们对每个点维护一棵包含其子树所有节点的主席树

询问(x,y)的时候用倍增找到(x,y)的lca,蓝后树上差分一下,即:

$total_size=sum[x]+sum[y]-sum[lca]-sum[fa[lca]]$

至于合并两棵树........我们把小的那棵树接到大的那棵上,并把小树上的主席树都重构一遍

这就是启发式合并,单次合并的复杂度大概为$O(logn)$

数据......离散化一下就好辣

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
inline void Swap(int &a,int &b){a^=b^=a^=b;}
void read(int &x){
    static char c=getchar();x=0; int f=1;
    while(c<'0'||c>'9') f=f&&(c!='-'),c=getchar();
    while('0'<=c&&c<='9') x=x*10+(c^48),c=getchar();
    x=f?x:-x;
}
#define N 80005
#define W 20000005
int TAT,n,m,T,tn,a[N],b[N],w[N],Rt[N],Siz[N],Pre;
int u,rt[N],lc[W],rc[W],siz[W];
int fa[N],Fa[18][N],d[N];
int cnt,hd[N],nxt[N<<1],ed[N],poi[N<<1];
bool vis[N];
inline void adde(int x,int y){
    nxt[ed[x]]=++cnt, hd[x]=hd[x]?hd[x]:cnt,
    ed[x]=cnt, poi[cnt]=y;
}
inline int Id(int x){return lower_bound(b+1,b+tn+1,x)-b;}
#define mid (l+r)/2
void Ins(int &o,int p,int l,int r,int x){
    if(!o) o=++u;
    siz[o]=siz[p]+1;
    if(l==r) return ;
    if(x<=mid) rc[o]=rc[p],Ins(lc[o],lc[p],l,mid,x);
    else lc[o]=lc[p],Ins(rc[o],rc[p],mid+1,r,x);
}
int Ask(int x,int y,int c1,int c2,int l,int r,int k){
    if(l==r) return b[l];
    int tmp=siz[lc[x]]+siz[lc[y]]-siz[lc[c1]]-siz[lc[c2]];
    if(k<=tmp) return Ask(lc[x],lc[y],lc[c1],lc[c2],l,mid,k);
    else return Ask(rc[x],rc[y],rc[c1],rc[c2],mid+1,r,k-tmp);
}
void dfs(int x,int RT){
    Fa[0][x]=fa[x]; d[x]=d[fa[x]]+1; vis[x]=1;
    for(int i=1;i<=17;++i) Fa[i][x]=Fa[i-1][Fa[i-1][x]];//dfs的同时处理掉倍增数组
    ++Siz[RT]; Rt[x]=RT;
    Ins(rt[x],rt[fa[x]],1,tn,Id(a[x]));
    for(int i=hd[x];i;i=nxt[i])
        if(poi[i]!=fa[x])
            fa[poi[i]]=x,dfs(poi[i],RT);
}
int LCA(int x,int y){
    if(d[x]<d[y]) Swap(x,y);
    for(int i=17;i>=0;--i) if(d[Fa[i][x]]>=d[y]) x=Fa[i][x];
    if(x==y) return x;
    for(int i=17;i>=0;--i) if(Fa[i][x]!=Fa[i][y]) x=Fa[i][x],y=Fa[i][y];
    return Fa[0][x];
}
int main(){
    register int i; char opt[3]; int q1,q2,q3,lca;
    read(TAT);read(n);read(m);read(T);
    for(i=1;i<=n;++i) read(a[i]),w[i]=a[i];
    while(m--) read(q1),read(q2),adde(q1,q2),adde(q2,q1);
    sort(w+1,w+n+1); b[tn=1]=w[1];
    for(i=2;i<=n;++i) if(w[i]!=w[i-1]) b[++tn]=w[i]; //离散化
    for(i=1;i<=n;++i) if(!vis[i]) dfs(i,i);
    while(T--){
        scanf("%s",opt);read(q1);read(q2);q1^=Pre;q2^=Pre;
        if(opt[0]=='L'){
            adde(q1,q2),adde(q2,q1);
            if(Siz[Rt[q1]]<Siz[Rt[q2]]) Swap(q1,q2);
            fa[q2]=q1; dfs(q2,Rt[q1]);//启发式合并,小树连到大树上
        }else{
            read(q3); q3^=Pre; lca=LCA(q1,q2);
            Pre=Ask(rt[q1],rt[q2],rt[lca],rt[fa[lca]],1,tn,q3);
            printf("%d\n",Pre);
        }
    }return 0;
}

猜你喜欢

转载自www.cnblogs.com/kafuuchino/p/10604911.html