suxxsfe

一言(ヒトコト)

bzoj4998 星球联盟(lct+并查集维护动态双连通性)

https://darkbzoj.tk/problem/4998
真真是调了一晚上啊。。。写这个blog的时候已经第二天一点了。。看着dbzoj拿着那缓慢至极的速度一个个点的测,测着测着就停了然后零分,自闭

\(n\) 个点,起初存在 \(m\) 条边,然后有 \(p\) 次操作,每次建一条边,问建成后,这个边的两个端点是否处于同一双连通分量中,如果是,输出双连通分量大小

考虑到没有删边操作,那么如果有两个点连通或双连通了,那么之后一定会继续连通或双连通,所以就可以将这样的一些点缩成一个点
于是我们使用两个并查集,一个维护连通性,一个维护双连通性
在我的写法中,其实更偏向于是维护双连通性的那个套在 lct 中,而维护联通性的是单独的

就是每当要连接两个点时,如果发现他们已经连通了,那么从一个点开始 dfs(当然另一个点还是要 makeroot,然后这个点也要 access 并 splay 它来把它伸展上去,都是 lct 基本操作了),把能走到的节点,都合并到这个节点的集合里(维护双连通性的集合),顺便统计答案,更新 \(size\)(发现这样的话也就不用 pushup 了)
这样,由于连接这两个端点时,产生了一些双连通分量,把这些分量都缩成一个点(就是开始 dfs 的那个点,并且是通过并查集来维护,所以其它点的并查集父亲都指向那个点),那么我们在 lct 中要找的一个节点 \(x\),实际上就是对于维护双连通性的那个并查集的 \(find(x)\)
这里一定注意别写错了,记得 lct 的点先 find 一下,我就是因为 notroot 函数里的点每 find 导致开始了一段漫长的调错。。。。
因为缩点后肯定不会存在双连通的点了,也就可以正常按处理树上问题的的思维来处理 lct 了,明白了这一点会更容易想

那如果它们还没有连同,当然是用 lct 的操作把他们连上边,然后再在维护连通性的那个并查集里把他们划分为同一个集合

还有就是一开始的那 \(m\) 条边也会构成双连通,我直接当不会写的。。可能只有我会犯这种错吧。。,然后又因此调了半天

#include<cstdio>
#include<algorithm>
#include<iostream>
#include<cmath>
#include<map>
#include<iomanip>
#include<cstring>
#define reg register
#define EN std::puts("")
#define LL long long
inline int read(){
	register int x=0;register int y=1;
	register char c=std::getchar();
	while(c<'0'||c>'9'){if(c=='-') y=0;c=std::getchar();}
	while(c>='0'&&c<='9'){x=x*10+(c^48);c=std::getchar();}
	return y?x:-x;
}
#define N 200005
struct tr{
	tr *fa,*son[2];
	int size,tag;
	tr *setfa;
}*null,dizhi[N],*pos[N];
int fa[N];
inline tr *find(tr *k){return (k->setfa==k)?k:k->setfa=find(k->setfa);}
inline int find(int k){return k==fa[k]?k:fa[k]=find(fa[k]);}
#define ident(tree,fa) (fa->son[1]==tree)
#define notroot(tree) (find(tree->fa)->son[0]==tree||find(tree->fa)->son[1]==tree)//一定要写 find(tree->fa) 而不是 tree->fa 啊。。。
inline void connect(tr *tree,tr *fa,int k){tree->fa=fa;fa->son[k]=tree;}
inline void pushdown(tr *tree){
	if(!tree->tag) return;
	tree->tag=0;
	std::swap(tree->son[0],tree->son[1]);
	tree->son[0]->tag^=1;tree->son[1]->tag^=1;
}
inline void rotate(tr *tree){
	tr *fa=find(tree->fa),*faa=find(fa->fa);
	pushdown(fa);pushdown(tree);
	int k=ident(tree,fa);
	connect(tree->son[k^1],fa,k);
	tree->fa=faa;
	if(notroot(fa)) faa->son[ident(fa,faa)]=tree;
	connect(fa,tree,k^1);
}
inline void splay(tr *tree){
	reg tr *fa,*faa;
	while(notroot(tree)){
		fa=find(tree->fa);faa=find(fa->fa);
		if(notroot(fa)) ident(fa,faa)^ident(tree,fa)?rotate(tree):rotate(fa);
		rotate(tree);
	}
}
inline void access(reg tr *x){
	for(reg tr *lastx=null;x!=null;lastx=x,x=find(x->fa)){
		pushdown(x);
		splay(x);
		x->son[1]=lastx;
	}
}
inline void makeroot(tr *tree){
	access(tree);
	splay(tree);tree->tag^=1;
}
int ans;
void dfs(tr *x,tr *pre){
	if(x==null) return;
	ans+=x->size;
	if(x!=pre) pre->size+=x->size,x->setfa=pre;
	pushdown(x);
	dfs(x->son[0],pre);dfs(x->son[1],pre);
}
inline void link(tr *x,tr *y){
	makeroot(x);
	ans=0;
	access(y);splay(y);
	dfs(y,y);
	printf("%d\n",ans);
}
inline void Link(int uu,int vv){
	ans=0;
	tr *u=find(pos[uu]),*v=find(pos[vv]);
	if(find(uu)!=find(vv)){
		fa[fa[uu]]=fa[vv];
		makeroot(u);u->fa=v;
	}
	else{
		makeroot(u);
		access(v);splay(v);
		dfs(v,v);
	}
}
int main(){
//		std::freopen("1.in","r",stdin);
//		std::freopen("out","w",stdout);
	int n=read(),m=read(),p=read();
	null=&dizhi[0];
	null->setfa=null;
	for(reg int i=1;i<=n;i++){
		pos[i]=&dizhi[i];
		dizhi[i].son[0]=dizhi[i].son[1]=dizhi[i].fa=null;
		dizhi[i].size=1;
		dizhi[i].setfa=&dizhi[i];
		fa[i]=i;
	}
	int uu,vv;
	for(reg int i=1;i<=m;i++){
		uu=read();vv=read();
		Link(uu,vv);
	}
	while(p--){
		uu=read();vv=read();
		Link(uu,vv);
		if(ans) printf("%d\n",ans);
		else puts("No");
	}
	return 0;
}
posted @ 2020-08-11 01:17  suxxsfe  阅读(235)  评论(0编辑  收藏  举报