【题面】
传送门:Welcome Party
【题解】
题意:有n个人编号从1-n,m对朋友关系,当一个人入场时发现场内没有认识的朋友就会不高兴,首先输出最小不高兴值,然后输出字典序最小的入场顺序使得n个人的不高兴值最小。
思路:要使不高兴值最小,那么当入场一个人p,那么p的朋友要跟着入场,p的朋友的朋友也要跟着入场,这样就能使得不高兴值最小,只有1。很容易想到用并查集判断n个人中有几个不相关的集合,即为最小的不高兴值。为使字典序最小,很容易想到用优先队列维护当前最小可入场的人的编号。注意p进去了并不代表p的朋友的朋友可以进去,必须看到直接的朋友才会高兴,所以每次进去一个人,只把他的所有直接朋友压入优先队列。在每次取优先队列的头元素时要注意判断剩余集合的最小元素是否有比头元素字典序小的,考虑到这一点我们在连接两个人时优先让字典序比较小的成为父节点。
测试数据:
input
4 2
1 3
2 4
output
2
1 2 3 4
【代码】
#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int maxn=1e6+5;
int pre[maxn];
int vis[maxn];
vector <int> vec[maxn];
vector <int> ans;
int Find(int x)
{
return x==pre[x]?x:pre[x]=Find(pre[x]);
}
void join(int x,int y)
{
x=Find(x),y=Find(y);
if(x<y) pre[y]=x;
else if(y<x) pre[x]=y;
}
int main()
{
priority_queue < int,vector<int>,greater<int> > q;
int t; scanf("%d",&t);
while(t--){
ans.clear();
int n,m; scanf("%d%d",&n,&m);
while(!q.empty()) q.pop();
for(int i=1;i<=n;i++) vis[i]=0,pre[i]=i,vec[i].clear();
while(m--){
int a,b; scanf("%d%d",&a,&b);
join(a,b);
vec[a].push_back(b);
vec[b].push_back(a);
}
for(int i=1;i<=n;i++)
if(Find(i)==i)
ans.push_back(i);
printf("%d\n",ans.size());
int cnt=0,cou=0;
while(cnt<n){
int pos=ans[cou++];
q.push(pos); vis[pos]=1;
while(!q.empty()){
pos=q.top();
if(cou<ans.size()&&ans[cou]<pos){
pos=ans[cou++];
vis[pos]=1;
}
else q.pop();
if(cnt++) printf(" ");
printf("%d",pos);
for(int i=0;i<vec[pos].size();i++){
int x=vec[pos][i];
if(!vis[x]){
vis[x]=1;
q.push(x);
}
}
}
}
puts("");
}
return 0;
}