CF1833E 题解
Geoyster
·
·
题解
CF1833E Round Dance
比赛做出来了,但是看别人都是直接跑图,这里提供一种并查集做法。
题目大意
## 思路
显然知道相邻关系可以当作图来看待,但是我们只要知道最终的可能组数,不妨考虑并查集。
先当作最多组数考虑。只要将所有相邻关系合并,最后数组数就行了。
再考虑最少组数。即将能和并的尽量合并。那么如何确定一个组能否去合并呢?以下分两种情况
1. 组内人数 $\geq 3$ 当边数等于点数时形成环,在题目背景下即相邻关系数等于组内人数。不能再合并。不形成环则可以。
2. 组内人数 $=2$ 无论如何都可以与其他组合并,应为确定相邻关系至多一条。
然后将能和并的看成一组即为最小组数。
## 实现
[并查集](https://oi-wiki.org/ds/dsu/),其实基本都是套模板,判断构成环只要看要连接的两点是否已在一个根节点下,是则有环,反之无环。然后重边也要特判一下。关于不可合并组,我是在找到环后就记录,如果这个值比总组数小则必然又可以构成一个可合并的组。
## AC CODE
```
#include<bits/stdc++.h>
using namespace std;
int t,n;
struct dsu{
int fa[200005],n;
void ini(){
for(int i=1;i<=n;i++) fa[i]=i;
}
int find(int a){
return fa[a]==a?a:fa[a]=find(fa[a]);
}
int cnt(){
int res=0;
for(int i=1;i<=n;i++) if(fa[i]==i) res++;
return res;
}
};
void solve(){
cin>>n;
dsu d; d.n=n; d.ini();
int mn=0,mx=n;
map<pair<int,int>,bool> exist;
for(int i=1;i<=n;i++){
int u; cin>>u;
if(exist[make_pair(u,i)]) continue;
exist[make_pair(i,u)]=1;
int t1=d.find(u),t2=d.find(i);
if(t1==t2) mn++;
else d.fa[t1]=t2;
}
mx=d.cnt();
if(mx>mn) mn++;
printf("%d %d\n",mn,mx);
}
signed main(){
ios::sync_with_stdio(0);
cin.tie(0);
cin>>t;
while(t--) solve();
return 0;
}
```