CF292D

提供一个 \(O(n^2+k)\) 的解法。

我们从左到右加边,发现其实大部分边都是 无用的 ,因为它不能让联通分量的个数改变,发现 有用的 边只有 \(O(n)\) 个,那么我们就记录下来有用的边的下标,从右到左加边的情况同理。

预处理前 \(i\) 个有用边和后 \(j\) 个有用边存在时的联通分量个数,可以做到 \(O(n^2)\) ,每次询问就找最近的有用边就完了。

代码如下:


// El Psy Kongroo

#include<bits/stdc++.h>
#define ll long long
#define PI pair<int,int>
#define fi first
#define se second
#define mp make_pair
#define ui unsigned int
#define pb push_back
using namespace std;
const int N=10110,P=510;
int n,m,k,fa[N];
int usef[N],usef2[N];
vector<int> u1,u2;
void init(){for(int i=1;i<=n;i++)fa[i]=i;}
int find(int x){
	if(fa[x]!=x)fa[x]=find(fa[x]);
	return fa[x];
}
void join(int x,int y){
	int fx=find(x),fy=find(y);
	fa[fx]=fy;
}
PI edg[N];
int f[P][P];
int wz1[N],wz2[N];
int main(){
	scanf("%d%d",&n,&m);
	for(int i=1;i<=m;i++){
		scanf("%d%d",&edg[i].fi,&edg[i].se);
	}
	init();
	int bef=0;
	u1.push_back(0);
	for(int i=1;i<=m;i++){
		wz1[i]=bef;
		if(find(edg[i].fi)!=find(edg[i].se)){
			join(edg[i].fi,edg[i].se);
			usef[i]=1;
			u1.push_back(i);
			bef++;
		}
	}
	init();
	bef=0;
	u2.push_back(0);
	for(int i=m;i>=1;i--){
		wz2[i]=bef;
		if(find(edg[i].fi)!=find(edg[i].se)){
			join(edg[i].fi,edg[i].se);
			usef2[i]=1;
			u2.push_back(i);
			bef++;
		}
	}
	int len=u1.size(),len2=u2.size();
	for(int i=0;i<len;i++){
		init();
		int ans=n;
		for(int o=1;o<=i;o++){
			join(edg[u1[o]].fi,edg[u1[o]].se);
			ans--;
		}
		for(int p=0;p<len2;p++){
			if(find(edg[u2[p]].fi)!=find(edg[u2[p]].se)){
				join(edg[u2[p]].fi,edg[u2[p]].se);
				ans--;
			}
			f[i][p]=ans;
		}
	}
	scanf("%d",&k);
	for(int i=1;i<=k;i++){
		int x,y;
		scanf("%d%d",&x,&y);
		printf("%d\n",f[wz1[x]][wz2[y]]);
	}
	return 0;
}
posted @ 2022-01-04 19:36  lytqwq  阅读(62)  评论(1)    收藏  举报