2020牛客多校第八场I题Interesting Computer Game(并查集求连通分量)
题目链接 https://ac.nowcoder.com/acm/contest/5673/I
题意:输入n对数,选n次,你每次可以从一对数中选一个,输出最多可以选到多少个不同的数。
题解:如果我们把每个数字当做点,n对数当做边,我们可以建立一个图,求这个图的连通分量,判断连通分量是否是环,如果是环ans+=这个连通块大小 ,如果不是环ans+=这个连通块大小-1。这样的思路我们可以用带权并查集实现,size表示连通区块的大小,vis表示该连通区块是不是环。因为该题数字的范围大,所有我们要先进行离散化。
#include <bits/stdc++.h> #define inf 0x3f3f3f3f using namespace std; typedef long long ll; const int maxn=5e5+7; const ll mod =1e9+7; int a[maxn],b[maxn]; int fa[maxn],size[maxn],vis[maxn]; int n,t; vector<int> p; ll ans; int find_fa(int x){ if(fa[x]==x) return x; else return fa[x]=find_fa( fa[x]); } int get_id(int x){ return lower_bound(p.begin(),p.end(),x)-p.begin()+1; } int main(){ scanf("%d",&t); int k=1; while(t--){ scanf("%d",&n); for(int i=1;i<=2*n+7;i++){ fa[i]=i; size[i]=1; vis[i]=0; } for(int i=1;i<=n;i++){ scanf("%d%d",&a[i],&b[i]); p.push_back(a[i]); p.push_back(b[i]); } sort(p.begin(),p.end()); //离散化 p.erase( unique(p.begin(),p.end()),p.end() ); int m=p.size(); ans=0; for(int i=1;i<=n;i++){ int ida=get_id(a[i]); int idb=get_id(b[i]); int aa=find_fa( ida ); int bb=find_fa( idb ); // cout<<"! "<<aa<<" "<<bb<<endl; if(aa!=bb){ fa[aa]=bb; size[bb]+=size[aa]; vis[bb]=vis[aa]|vis[bb]; }else{ vis[aa]=1; } } for(int i=1;i<=m;i++){ int faz=find_fa(i); if(vis[faz]) ans++,vis[faz]=0; if(fa[i]==i) ans+=size[i]-1; } printf("Case #%d: %d\n",k++,ans); p.clear(); } return 0; }