AT_abc447_E 并查集的应用

题目大意,就是给你一个图,要求这个图的联通块要等于两个,你可以做的操作是把其中的某几条线给删除掉

image
*输入
5 7
2 3
1 2
1 5
4 5
2 4
3 5
1 3
输出
22

解题思路

每条边的边权时2^i次方(1<=i<=m)
策略是要尽可能的保住边权大的那条边,之所以从开始就是为了保住i更大的一方,因为27>26+25+24…………,所以保住i更大的也就是边权更大的那一条边非常重要
这个题目跟联通块相关联,所以非常适合用并查集(我自己这么觉得的)

code

#include<bits/stdc++.h>
using namespace std;
//"O campeão tem nome, e se chama Charles Oliveira!"
#define int long long
#define endl '\n'
#define emplace ep
#define pob 
#define ll long long
#define pb push_back
#define pof pop_front
#define pob pop_back
#define all(a) a.begin(),a.end()
#define rall(a) a.rbegin(),a.rend()

using ld = long double;
using ui = unsigned;
using ull = unsigned long long;
using i128 = __int128;
const int N=200005;
int mod=998244353;
int MOD=1000000007;
int n,m,u[N],v[N],ans,vis[N],fa[N];
void init(int n){
    for(int i=1;i<=n;i++){
        fa[i]=i;
    }
    return ;
}
int find(int x){
    return(fa[x]==x?x:fa[x]=find(fa[x]));
}
void uni(int x,int y){
    int fx=find(x);
    int fy=find(y);
    if(fx!=fy){
        fa[fx]=fy;
    }
    return ;
}
int fast_pow(int n,int x){
    int res=1;
    while(x){
        if(x&1){
            res=(res*n)%mod;
        }
        n=(n*n)%mod;
        x>>=1;
    }
    return res%mod;
}
void solve() {
    cin>>n>>m;
    init(n);
    for(int i=1;i<=m;i++){
        cin>>u[i]>>v[i];
    }
    //之所以从开始就是为了保住i更大的一方,因为2^7>2^6+2^5+2^4…………
    //所以保住i更大的也就是边权更大的那一条边非常重要
    for(int i=m,cnt=n;i>=1;i--){
        if(cnt>2&&find(u[i])!=find(v[i])){
            uni(fa[u[i]],fa[v[i]]);
            cnt--;
            vis[i]=1;//这个不去掉
        }
        else if(find(u[i])==find(v[i])){
            //如果父亲节点都是一样的话,去掉和不去掉都不会影响联通块的数量
            vis[i]=1;
        }
    }
    for(int i=1;i<=m;i++){
        if(vis[i]==0){
            ans=(ans+fast_pow(2,i))%mod;
        }
    }
    cout<<ans<<endl;
}
signed main(){
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    cout.tie(nullptr);
    int t=1;
    //cin>>t;
    while(t--)solve();
}

代码解释

先是并查集的标准开头
void init
void find
void unit

核心代码及注释

for(int i=m,cnt=n;i>=1;i--){
        if(cnt>2&&find(u[i])!=find(v[i])){
            uni(fa[u[i]],fa[v[i]]);//当cnt>2的时候,这个大的边可以留着,对于大的边,只要可以留那就留
//这两个的父节点不一样的话,那就直接把这两个连接起来,然后cnt--,vis[1]代表保留
            cnt--;
            vis[i]=1;//这个不去掉
        }
        else if(find(u[i])==find(v[i])){
            //如果父亲节点都是一样的话,去掉和不去掉都不会影响联通块的数量
            vis[i]=1;
        }
    }
posted @ 2026-03-13 13:00  Time_q  阅读(2)  评论(0)    收藏  举报