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;
}

浙公网安备 33010602011771号