292-D 题目大意
给定一张无向图,由\(n\)个顶点\(m\)条边。有\(q\)次询问,每次询问将\([l,r]\)的边删去,问图中有多少连通分量。
Solution
涉及连通分量,尝试应用并查集解决。
记录一个前缀并查集\(pre[i]\),表示前\(i\)条边连通后的图;和一个后缀并查集\(suf[i]\),表示后\(m-i\)条边连通后的图。
对于一个询问\([l,r]\),把\(pre[l-1]\)和\(suf[r+1]\)两个并查集拼起来,再统计连通分量即可。
时间复杂度\(O((m+qn)α(n))\)。
#include<bits/stdc++.h>
using namespace std;
using ll=long long;
template<typename T>
struct DSU{
int n;
vector<T> p,siz;
DSU(int _n):p(_n+1),siz(_n+1),n(_n){
iota(p.begin(),p.end(),0);
for(int i=0;i<=n;i++) siz[i]=1;
}
T findd(T x){
return p[x]==x?x:p[x]=findd(p[x]);
}
void unionn(T x,T y){
x=findd(x),y=findd(y);
if(x==y) return;
if(siz[x]>siz[y]) swap(x,y);
p[x]=y;
siz[y]+=siz[x];
}
};
void solve(){
int n,m;
cin>>n>>m;
vector<DSU<int>> pre(m+2,DSU<int>(n)),suf(m+2,DSU<int>(n));
vector<pair<int,int>> e(m);
for(int i=0;i<m;i++){
int x,y;
cin>>x>>y;
e[i]={x,y};
}
for(int i=1;i<=m;i++){
auto [x,y]=e[i-1];
pre[i]=pre[i-1];
pre[i].unionn(x,y);
}
for(int i=m;i;i--){
auto [x,y]=e[i-1];
suf[i]=suf[i+1];
suf[i].unionn(x,y);
}
int q;
cin>>q;
while(q--){
int l,r;
cin>>l>>r;
auto u=pre[l-1],v=suf[r+1];
for(int i=1;i<=n;i++){
u.unionn(i,v.findd(i));
}
int ans=0;
for(int i=1;i<=n;i++){
if(u.p[i]==i){
ans++;
}
}
cout<<ans<<'\n';
}
}
int main(){
ios_base::sync_with_stdio(0);cin.tie(0);cout.tie(0);
//freopen("input.txt","r",stdin);
//freopen("output.txt","w",stdout);
int T=1;
//cin>>T;
while(T--){
solve();
}
return 0;
}
标签:pre,suf,DSU,int,siz,查集,CF,292
From: https://www.cnblogs.com/fengxue-K/p/17981825