I Interesting Computer Game
题目传送门
思路
将每次输入的a和b当做点,将其连成一条边,很明显的对于形成的连通块来说,如果无环就只能取到当前连通块的顶点减一的点,如果连通块内存在至少一个环,即可将该连通块取完,所以我们只需要判断连通块有无环
同时我们注意到数据的范围为
所以要采取离散化的方法将数据处理一下,然后对于连通块采取并查集的方式进行维护
AC Code
#include<cstdio>
#include<iostream>
#include<algorithm>
using namespace std;
const int N=1e5 +9;
// #define TDS_ACM_LOCAL
int n, vis[N<<1], ls[N<<1], fa[N<<1];
int x, y, len, cnt, ans, idx;
pair<int,int>p[N];
int find(int x){ //查找并查集的祖先
if(x==fa[x]) return x;
return fa[x]=find(fa[x]);
}
void solve(){
cin>>n;
len=0;
for(int i=1; i<=n; i++){
cin>>p[i].first>>p[i].second;
ls[++len]=p[i].first, ls[++len]=p[i].second; //ls离散化数组
}
sort(ls+1, ls+1+len); //排序
cnt=unique(ls+1, ls+1+len)-ls-1; //去重并且求得不同元素的个数
for(int i=1; i<=cnt; i++) fa[i]=i, vis[i]=0;
for(int i=1; i<=n; i++){
p[i].first=lower_bound(ls+1, ls+1+cnt, p[i].first)-ls; //更新原数组的元素
p[i].second=lower_bound(ls+1, ls+1+cnt, p[i].second)-ls;
x=find(p[i].first), y=find(p[i].second); //找到相应的祖先
if(x!=y){
fa[y]=x;
if(vis[x] || vis[y]) vis[x]=1, vis[y]=0; //当有环的连通块遇到其他的连通块时,将祖先标记为1
}
else vis[x]=1; //相同祖先结点即为在一个连通块,即为行成环
}
ans=cnt; //ans先取最大(即为a和b中不同元素的个数)
for(int i=1; i<=cnt; i++) if(fa[i]==i && !vis[i]) ans--; //如果为祖先但是为0(即为无环),ans--
cout<<"Case #"<<++idx<<": "<<ans<<endl;
return ;
}
int main(){
ios::sync_with_stdio(0);
cin.tie(0), cout.tie(0);
#ifdef TDS_ACM_LOCAL
freopen("D:\\VS code\\.vscode\\testall\\in.txt", "r", stdin);
freopen("D:\\VS code\\.vscode\\testall\\out.txt", "w", stdout);
#endif
int T;
cin>>T;
while(T--) solve();
return 0;
}