[BZOJ 3123] [SDOI 2013]森林(可持久化线段树+并查集+启发式合并)

[BZOJ 3123] [SDOI 2013]森林(可持久化线段树+启发式合并)

题面

给出一个n个节点m条边的森林,每个节点都有一个权值。有两种操作:

  1. Q x y k查询点x到点y路径上所有的权值中,第k小的权值是多少。此操作保证点x和点y连通,同时这两个节点的路径上至少有k个点。
  2. L x y在点x和点y之间连接一条边。保证完成此操作后,仍然是一片森林。

分析

用并查集维护连通性以及每个联通块的大小

用主席树维护路径上第k大,第x棵主席树维护的是节点x到根的链上权值的出现情况,类似[BZOJ2588]Count on a tree(LCA+主席树)。不过这道题不用dfs序,直接根据编号建树。

合并x,y的时候启发式合并。若x子树大小比y小,就重新dfs一遍x的子树,并把y到根的链上权值加进去。反之同理。详情见代码。时间复杂度\(O(n \log ^2n)\)

代码

#include<iostream>
#include<cstdio>
#include<cstring> 
#include<algorithm>
#include<cmath>
#include<vector>
#define INF 0x3f3f3f3f
#define maxn 100000
#define maxlogn 22
#define maxnlogn 10000000
using namespace std;
inline void qread(int &x){
	x=0;
	int sign=1;
	char c=getchar();
	while(c<'0'||c>'9'){
		if(c=='-') sign=-1;
		c=getchar();
	}
	while(c>='0'&&c<='9'){
		x=x*10+c-'0';
		c=getchar();
	}
	x=x*sign;
}
inline void qprint(int x){
	if(x<0){
		putchar('-');
		qprint(-x);
	}else if(x==0){
		putchar('0');
		return; 
	}else{
		if(x>=10) qprint(x/10);
		putchar('0'+x%10);
	}
}

int testcase;//是测试点编号,不是数据组数... 
int n,m,q,mv;
int val[maxn+5];
int tmp[maxn+5];
int discrete(int *a,int n){
	int sz=0;
	for(int i=1;i<=n;i++) tmp[i]=a[i];
	sort(tmp+1,tmp+1+n);
	sz=unique(tmp+1,tmp+1+n)-tmp-1;
	for(int i=1;i<=n;i++){
		a[i]=lower_bound(tmp+1,tmp+1+sz,a[i])-tmp;
	}
	return sz;
}

struct edge{
	int from;
	int to;
	int next;
}E[maxn*2+5];
int head[maxn+5];
int esz;
void add_edge(int u,int v){
	esz++;
	E[esz].from=u;
	E[esz].to=v;
	E[esz].next=head[u];
	head[u]=esz;
	esz++;
	E[esz].from=v;
	E[esz].to=u;
	E[esz].next=head[v];
	head[v]=esz;
}

struct disjoint_set{
	int fa[maxn+5];
	int sz[maxn+5];
	void ini(int n){
		for(int i=1;i<=n;i++){
			fa[i]=i;
			sz[i]=1;
		}
	}
	int find(int x){
		if(fa[x]==x) return x;
		else return fa[x]=find(fa[x]);
	}
	inline int size(int x){//返回x所在联通块大小 
		return sz[find(x)];
	}
	void merge(int x,int y){
		int fx=find(x),fy=find(y);
		if(sz[fx]>sz[fy]) swap(fx,fy);
		fa[fx]=fy;
		sz[fy]+=sz[fx]; 
	} 
}S;

struct persist_segment_tree{
#define lson(x) (tree[x].ls)
#define rson(x) (tree[x].rs)
	struct node{
		int ls;
		int rs;
		int val;
	}tree[maxnlogn+5];
	int ptr;
	inline void push_up(int x){
		tree[x].val=tree[lson(x)].val+tree[rson(x)].val;
	} 
	void update(int &x,int last,int upos,int l,int r){
		x=++ptr;
		tree[x]=tree[last];
		if(l==r){
			tree[x].val++;
			return;
		}
		int mid=(l+r)>>1;
		if(upos<=mid) update(lson(x),lson(last),upos,l,mid);
		else update(rson(x),rson(last),upos,mid+1,r);
		push_up(x);
	}
	int query(int x,int y,int lc,int lcfa,int k,int l,int r){
		if(l==r){
			return l;
		}
		int mid=(l+r)>>1;
		int lcnt=tree[lson(x)].val+tree[lson(y)].val-tree[lson(lc)].val-tree[lson(lcfa)].val;
		if(k<=lcnt) return query(lson(x),lson(y),lson(lc),lson(lcfa),k,l,mid);
		else return query(rson(x),rson(y),rson(lc),rson(lcfa),k-lcnt,mid+1,r); 
	}
#undef lson
#undef rson
}T;
int root[maxn+5];

int log2n;
int deep[maxn+5];
int anc[maxn+5][maxlogn+5];
void dfs(int x,int fa){
	deep[x]=deep[fa]+1;
	anc[x][0]=fa;
	for(int i=1;i<=log2n;i++) anc[x][i]=anc[anc[x][i-1]][i-1];
	T.update(root[x],root[fa],val[x],1,mv);
	for(int i=head[x];i;i=E[i].next){
		int y=E[i].to;
		if(y!=fa){
			dfs(y,x);
		}
	} 
}
int lca(int x,int y){
	if(deep[x]<deep[y]) swap(x,y);
	for(int i=log2n;i>=0;i--){
		if(deep[anc[x][i]]>=deep[y]) x=anc[x][i];
	}
	if(x==y) return x;
	for(int i=log2n;i>=0;i--){
		if(anc[x][i]!=anc[y][i]){
			x=anc[x][i];
			y=anc[y][i];
		}
	}
	return anc[x][0];
}

void merge(int x,int y){
	if(S.size(x)>S.size(y)) swap(x,y);
	S.merge(x,y);
	add_edge(x,y);
	if(deep[y]==0) deep[y]=1;
	dfs(x,y);//注意不是dfs(x,0),因为x的父亲是y 
} 
int query(int x,int y,int k){
	int lc=lca(x,y);
	int id=T.query(root[x],root[y],root[lc],root[anc[lc][0]],k,1,mv);
	return tmp[id];
}


int main(){
//	freopen("5.in","r",stdin);
	int last=0;
	char op[2];
	int x,y,k;
	qread(testcase);
	qread(n);
	qread(m);
	qread(q);
	S.ini(n);
	log2n=log2(n)+1; 
	for(int i=1;i<=n;i++) qread(val[i]);
	mv=discrete(val,n);
	for(int i=1;i<=m;i++){
		qread(x);
		qread(y);
		add_edge(x,y);
		S.merge(x,y);
	} 
	for(int i=1;i<=n;i++){
		if(!anc[i][0]) dfs(i,0);
	}
	for(int i=1;i<=q;i++){
		scanf("%s",op);
		if(op[0]=='Q'){
			qread(x);
			qread(y);
			qread(k);
			x^=last;
			y^=last;
			k^=last;
			last=query(x,y,k);
			qprint(last);
			putchar('\n'); 
		}else{
			qread(x);
			qread(y);
			x^=last;
			y^=last;
			merge(x,y);
		}
	}
}
/*
1
8 4 8
1 1 2 2 3 3 4 4
4 7
1 8
2 4
2 1
Q 8 7 3 
Q 3 5 1
Q 10 0 0
L 5 4
L 3 2 
L 0 7
Q 9 2 5 
Q 6 1 6
*/
posted @ 2019-09-07 13:55  birchtree  阅读(245)  评论(0编辑  收藏  举报