题解:P12451 [INOI Team Selection 2021] Labelless Graph
Solution
初看这道题可能会觉得有些复杂,因为可能会涉及到图的重构相关内容。但是事实上,我们只需要分析所有连通块的大小。
思考一个简单的问题:如何判断这张图是否连通。
结论:一个点数
也就是说,至少存在两个点,使得删掉点之后图只剩下一个大小为
所以如果是连通图,直接返回
否则,我们就可以得到最大连通块的大小和数量,以及每个点是否在某一个最大连通块上。把它们全删掉再递归做即可。特别注意判断所有点所在连通块大小都相同的情况。
但是有一个非常恶心的事情:我们的做法实际上不对
我们只有确定了所有
时间复杂度:注意到你只会执行
#include<bits/stdc++.h>
#define ffor(i,a,b) for(int i=(a);i<=(b);i++)
#define roff(i,a,b) for(int i=(a);i>=(b);i--)
using namespace std;
const int MAXN=300+10;
int n,m,fa[MAXN],sze[MAXN];
set<int> ok;
multiset<int> res[MAXN];
vector<int> ans;
int find(int k) {return (fa[k]==k)?k:(fa[k]=find(fa[k]));}
int main() {
ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
cin>>n;
ffor(i,1,n) {
ffor(j,1,n) fa[j]=j,sze[j]=0;
cin>>m;
ffor(i,1,m) {int u,v;cin>>u>>v,fa[find(v)]=find(u);}
ffor(i,1,n) sze[find(i)]++;
ffor(j,1,n-1) if(find(j)==j) res[i].insert(sze[j]);
}
ffor(i,1,n) ok.insert(i);
vector<int> fst;
while(!ok.empty()) {
int al=ok.size();
if(al==1) {ans.push_back(1);break ;}
int cnt=0;
for(auto id:ok) if(*(--res[id].end())==al-1) cnt++;
if(cnt>=2) {
if(al==2) {
int flg=1;
for(auto id:fst) flg&=(*(res[id].begin())==1);
if(flg) ans.push_back(1),ans.push_back(1);
else ans.push_back(2);
}
else ans.push_back(al);
break ;
}
pair<int,int> mzx={0,0};
for(auto u:ok) {
int mx=*(--res[u].end()),cnt=0;
for(auto id:res[u]) cnt+=(id==mx);
mzx=max(mzx,{mx,cnt});
}
vector<int> del;
for(auto u:ok) {
int cnt=0;
for(auto id:res[u]) cnt+=(id==mzx.first);
if(mzx.second!=cnt) del.push_back(u);
}
ffor(i,1,mzx.second) ans.push_back(mzx.first);
if(del.size()==0) {ans.push_back(mzx.first);break ;}
if(fst.empty()) fst=del;
for(auto id:del) ok.erase(id);
for(auto id:ok) ffor(i,1,mzx.second) res[id].erase(res[id].find(mzx.first));
}
sort(ans.begin(),ans.end());
cout<<ans.size()<<'\n';
for(auto id:ans) cout<<id<<' ';
return 0;
}