CF-292-D-并查集

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;
}
posted @ 2024-01-23 10:37  fengxue-K  阅读(28)  评论(0)    收藏  举报