题面
输入样例
5 3
2 1
5 2
2 4
1 3
输出样例
1 2 3
题意
给定一棵全为白色结点的树,现在需要将 k k k 个结点依次染黑,但在染黑的过程中最多只能有一个丑陋的结点,丑陋的结点定义如下(需要同时满足三个条件):
1.该点本身为黑色
2.相邻结点中至少有一个为白色
3.相邻结点中最多只有一个为黑色
上述的 2、3 条件还可转化为:若某黑色结点的相邻结点中不存在白色结点,或其相邻结点有两个及以上的黑色结点,那么该结点就不是丑陋的结点。
问:现在需要将 k k k 个结点染为黑色,若存在可行方案,则输出该方案依次染黑的结点。
思路
手模后可以发现本题一定有解,原因是从任一叶节点开始染色时,下一步可染该叶节点的父亲,此时叶节点由于相邻结点中不存在白色结点而不再丑陋。设此时的丑陋结点为 u u u ,则下一次选择 u u u 的任一相邻结点 v v v 时, u u u 都将因相邻结点有两个及以上的黑色结点而不再丑陋,由此一直迭代下去。
考虑采用树的欧拉序,当找到第一个叶结点起开始,以欧拉序输出未染色的结点,则对于样例中将所有结点染色的次序为: 4 4 4 -> 2 2 2 -> 5 5 5 -> 1 1 1 -> 3 3 3
(当然,如果不想这么麻烦的话,本题的正解是输出以任一叶子作为根的 d f s dfs dfs 序)
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N=3e5+50;\
vector<int>g[N];
int n,k;
int euler[N*2],vis[N],vis2[N];
int sum[N],fat[N];
int num=0,cnt=0;
void dfs(int x,int fa){
sum[x]=1;
for(int i=0;i<g[x].size();i++){
int y=g[x][i];
if(fa==y)
continue;
// if(num<k){
// cout<<y<<" ";num++;
// }
dfs(y,x);
sum[x]+=sum[y];
fat[y]=x;
}
}
void euler_(int p){
euler[++cnt]=p;
vis2[p]=true;
for(int i:g[p]){
if(!vis2[i]){
euler_(i);
euler[++cnt]=p;
}
}
}
signed main(){
cin>>n>>k;
int x,y;
for(int i=1;i<n;i++){
cin>>x>>y;
g[x].push_back(y);
g[y].push_back(x);
}
dfs(1,0);
euler_(1);
int flag=0,num=0;
for(int i=1;i<cnt&&num<k;i++){
if(sum[euler[i]]==1){
vis[euler[i]]=1;
flag=1;
cout<<euler[i];
if(num!=k-1)
cout<<" ";num++;
continue;
}
if(flag==0)
continue;
if(vis[euler[i]]==0){
cout<<euler[i];
if(num!=k-1)
cout<<" ";
num++;
vis[euler[i]]=1;
}
}
return 0;
}
/*
10 10
1 2
1 3
1 4
4 6
4 7
5 8
5 9
5 10
2 5
*/