• ●BZOJ 3123 [Sdoi2013]森林


    题链:

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

    题解:

    主席树,在线,启发式合并
    简单版(只有询问操作):[2588: Spoj 10628. Count on a tree] [题解]

    多了一个合并联通块的操作,所以采用启发式合并。
    名字看似高大上,其实就是把小的那个联通块暴力连在大的上面。
    (别想多了,暴力就是暴力,一一重新遍历小的联通块的点,然后重新对这些点建立主席树)

    主席树启发式合并的总的复杂度:$Nlog_2^2N$
    应该是每次合并的两个东西大小都相同时,复杂度最高(但不会证明,现在只能先记着),
    在每个点需要花费的代价为 1 时,复杂度为 $Nlog_2N$,
    但是主席树的每个点需要花费的代价为 $log_2N$,所以总的复杂度为 $Nlog_2^2N$

    代码:

    #include<cstdio>
    #include<cstring>
    #include<iostream>
    #include<algorithm>
    #define MAXN 80500
    using namespace std;
    int A[MAXN],tmp[MAXN];
    int bel[MAXN],siz[MAXN],dep[MAXN],fa[MAXN][20];
    int N,M,T,tnt;
    struct Edge{
    	int to[MAXN*2],nxt[MAXN*2],head[MAXN],ent;
    	Edge(){
    		ent=2; memset(head,0,sizeof(head));
    	}
    	void Adde(int u,int v){
    		to[ent]=v; nxt[ent]=head[u]; head[u]=ent++;
    		to[ent]=u; nxt[ent]=head[v]; head[v]=ent++;
    	}
    }E;
    struct CMT{
    	int rt[MAXN],ls[MAXN*400],rs[MAXN*400],cnt[MAXN*400],sz;
    	void Insert(int v,int &u,int l,int r,int p){
    		u=++sz; ls[u]=ls[v]; 
    		rs[u]=rs[v]; cnt[u]=cnt[v];
    		cnt[u]++; if(l==r) return;
    		int mid=(l+r)>>1;
    		if(p<=mid) Insert(ls[v],ls[u],l,mid,p);
    		else Insert(rs[v],rs[u],mid+1,r,p);
    	}
    	int Query(int a,int b,int c,int d,int l,int r,int K){
    		if(l==r) return l;
    		int mid=(l+r)>>1,lcnt=cnt[ls[a]]+cnt[ls[b]]-cnt[ls[c]]-cnt[ls[d]];
    		if(K<=lcnt) return Query(ls[a],ls[b],ls[c],ls[d],l,mid,K);
    		else return Query(rs[a],rs[b],rs[c],rs[d],mid+1,r,K-lcnt);
    	}
    	void Reset(){
    		for(int u=1;u<=N;u++){
    			bel[u]=u; siz[u]=1; dep[u]=1;
    			Insert(rt[fa[u][0]],rt[u],1,tnt,A[u]);
    		}
    	}
    }DT;
    void read(int &x){
    	static int f; static char ch;
    	x=0; f=1; ch=getchar();
    	while(ch<'0'||'9'<ch){if(ch=='-')f=-1;ch=getchar();}
    	while('0'<=ch&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    	x=x*f;
    }
    int find(int u){
    	return u==bel[u]?u:bel[u]=find(bel[u]);
    }
    void dfs(int u,int dad){
    	memset(fa[u],0,sizeof(fa[u]));
    	dep[u]=dep[dad]+1; fa[u][0]=dad; 
    	for(int k=1;k<=18&&fa[fa[u][k-1]][k-1];k++)
    		fa[u][k]=fa[fa[u][k-1]][k-1];
    	DT.Insert(DT.rt[dad],DT.rt[u],1,tnt,A[u]);
    	for(int i=E.head[u],v;i;i=E.nxt[i]){
    		v=E.to[i]; if(v==dad) continue;
    		dfs(v,u);
    	}
    }
    void merge(int u,int v){
    	static int fu,fv;
    	fu=find(u); fv=find(v);
    	if(siz[fu]<siz[fv]) swap(u,v),swap(fu,fv);
    	E.Adde(u,v); bel[fv]=fu; siz[fu]+=siz[fv];
    	dfs(v,u);
    }
    int LCA(int x,int y){
    	if(dep[x]>dep[y]) swap(x,y);
    	for(int k=18;k>=0;k--)
    		if(dep[fa[y][k]]>=dep[x]) y=fa[y][k];
    	if(x==y) return x;
    	for(int k=18;k>=0;k--)
    		if(fa[x][k]!=fa[y][k]) x=fa[x][k],y=fa[y][k];
    	return fa[x][0];
    }
    void solve(){
    	int lastANS=0,a,b,c,d,rta,rtb,rtc,rtd,K; char ch;
    	for(int i=1;i<=T;i++){
    		scanf(" %c",&ch);
    		read(a); read(b);
    		a^=lastANS; b^=lastANS;
    		if(ch=='Q'){
    			read(K); K^=lastANS;
    			c=LCA(a,b); d=fa[c][0];
    			rta=DT.rt[a]; rtb=DT.rt[b];
    			rtc=DT.rt[c]; rtd=DT.rt[d];
    			lastANS=tmp[DT.Query(rta,rtb,rtc,rtd,1,tnt,K)];
    			printf("%d
    ",lastANS);
    		}
    		else merge(a,b);
    	}
    }
    int main(){
    	int testcase;read(testcase);
    	read(N); read(M); read(T);
    	for(int i=1;i<=N;i++)
    		read(A[i]),tmp[i]=A[i];
    	sort(tmp+1,tmp+N+1);
    	tnt=unique(tmp+1,tmp+N+1)-tmp-1;
    	for(int i=1;i<=N;i++)
    		A[i]=lower_bound(tmp+1,tmp+tnt+1,A[i])-tmp;
    	DT.Reset();
    	for(int i=1,u,v;i<=M;i++){
    		read(u); read(v);
    		merge(u,v);
    	}
    	solve();
    	return 0;
    }

  • 相关阅读:
    poj 1562 Oil Deposits
    poj 1650 Integer Approximation
    snmp4j 编程
    ubuntu 13.04 163源(亲测可用)
    c语言中static 用法总结(转)
    Spring入门
    Hibernate入门
    Struts2入门教程
    素数距离问题
    ASCII码排序
  • 原文地址:https://www.cnblogs.com/zj75211/p/8075961.html
Copyright © 2020-2023  润新知