CF292D Connected Components

题目

CF292D Connected Components

给出一个图,每次删除其中一个区间内的边,询问剩下的边构成的图当中的连通块个数。

\(N\le 500,1\le M,K\le 10000\)

分析

维护一个前缀的并查集和后缀的并查集,然后可以考虑每次拿出两段来合并,于是就很容易了。

具体可以见代码,时间复杂度是 \(O(kn\alpha(n))\)

代码

#include<bits/stdc++.h>
using namespace std;
template<typename T>
inline void read(T &x){
	x=0;bool f=false;char ch=getchar();
	while(!isdigit(ch)){f|=ch=='-';ch=getchar();}
	while(isdigit(ch)){x=x*10+(ch^48);ch=getchar();}
	x=f?-x:x;
	return ;
}
template<typename T>
inline void write(T x){
	if(x<0) x=-x,putchar('-');
	if(x>9) write(x/10);
	putchar(x%10^48);
	return ;
}
const int N=505,M=1e4+5;
#define PII pair<int,int>
#define pb push_back
#define fi first
#define se second
#define mp make_pair
#define ll long long
int n,m,k;
int l[M][N],r[M][N],fa[N];
int Getfa1(int id,int x){return l[id][x]==x?x:l[id][x]=Getfa1(id,l[id][x]);}
int Getfa2(int id,int x){return r[id][x]==x?x:r[id][x]=Getfa2(id,r[id][x]);}
int Getfa(int x){return fa[x]==x?x:fa[x]=Getfa(fa[x]);}
PII a[M];
signed main(){
	read(n),read(m);
	for(register int i=1;i<=n;i++) l[0][i]=r[m+1][i]=i;
	for(register int i=1;i<=m;i++) read(a[i].fi),read(a[i].se);
	for(register int i=1;i<=m;i++){
		for(register int j=1;j<=n;j++) l[i][j]=l[i-1][j];
		int x=Getfa1(i,l[i][a[i].fi]),y=Getfa1(i,l[i][a[i].se]);
		if(x==y) continue;
		l[i][x]=y;
	}
	for(register int i=m;i>=1;i--){
		for(register int j=1;j<=n;j++) r[i][j]=r[i+1][j];
		int x=Getfa2(i,r[i][a[i].fi]),y=Getfa2(i,r[i][a[i].se]);
		if(x==y) continue;
		r[i][x]=y;
	}
	read(k);
	while(k--){
		int lc,rc,Ans=0;
		read(lc),read(rc);
		for(register int i=1;i<=n;i++) fa[i]=l[lc-1][i];
		for(register int i=1;i<=n;i++){
			int x=Getfa(i),y=Getfa(r[rc+1][i]);
			if(x==y) continue;
			fa[x]=y;
		}
		for(register int i=1;i<=n;i++) if(Getfa(i)==i) Ans++;
		write(Ans),putchar('\n');
	}
	return 0;
}

扩展

更优秀的解法:gyh20的博客CF292D

使用主席树和 \(LCT\) 进行在线的处理,做到了 \(O((n+m+q)\log n)\)

posted @ 2021-09-14 09:44  __Anchor  阅读(35)  评论(0编辑  收藏  举报