题目链接:https://codeforces.com/contest/1228/problem/F
题目大意:
判断一个树是不是可以通过McDic’s generation得到,过程是在一个满二叉树中,删除一个非根的节点,如果这个非根的节点有孩子的话,接到改节点的父亲身上。
题目思路:
这道题想了会儿想不出来,然后看了很多博客都觉得有点难理解,直到我看到一篇博客:传送门,这篇博客的方法很精炼,跟我看到的其他博客的方法略有不同,很好理解,推荐大家读一读,这题的主要解决思想我是从这学到的。
开始题目讲解。首先是分类,一共有三种情况,第一种是删除的是根节点的儿子,第二种是删除的是普通节点(就是不是根节点,也不是其他两种节点的节点),第三种是删除叶节点,大部分博客都是这么分类的,但是用我们这种做法可以将第二第三种方法合并在一起,也就是只用讨论两个情况:根节点的儿子 or 非根节点的其他节点。
跟推荐博客一样使用官方题解的图片:传送门
删除的是根节点的儿子节点时:
当删除根节点的儿子时,这种情况下,直径长度为偶数,可以看做是两个满二叉树的根接在一起形成的图,而这种情况下,答案就是两个这满二叉树的根节点。
删除的不是根节点的儿子节点时:
这种情况下,直径是奇数,可能的根节点就只能是最中间的那个点。
所以做法第一步,是得到直径,这里用的方法是先dfs一遍得到距离1最远的点,这一定是直径上的点,叫s,然后从s开始dfs,得到距离s最远的点t,这样s到t的路就是直径,两遍dfs得到直径属于树的基本性质,证明可以去网上找资料。然后从s开始dfs,用一个g记录路径,这一部分的目的是得到s到t的路径,如果是偶数个点,那就是第一种情况,否则是第二种。
先讨论第一种情况,刚才说过,这种情况就是变成两棵满二叉树,它们的根节点就是直径的最中间两个点,然后就是判断这两个根节点各自的子树是否为满二叉树,由于是中点,所以只要是满二叉树一定一样大。
我判断满二叉树的方法是,一直往下走,然后把每个节点的儿子都记录下来,如果有一个节点的儿子数量不是0或2.那么直接凉凉,如果是2,比较两个儿子的子树大小是否相等,如果不相等直接凉凉,除此之外都可以。
推荐博客代码它的判断方法是,前面都一样,但是它遇到儿子数量是2的时候,不是比较儿子的子树大小,而是比较儿子的儿子数量。之前我想到一个反例如图所示,但是经过跟博主的讨论后我意识到按照题目要求给不出这种情况。
之前想到的推荐博客做法的反例:
第二种情况跟第一种情况很相似,唯一的区别是,第一次遇到儿子数量不是0或2的时候,把这个点标记为答案,之后要是再遇到儿子数量不是0或2的时候就直接凉凉。同时答案是说它是那个被删节点的父亲,所以它的子树大小要额外++,其他的与第一种情况一致。
以下是代码:
#include<bits/stdc++.h>
using namespace std;
#define inf 0x3f3f3f3f
#define rep(i,a,b) for(int i=a;i<=b;i++)
#define per(i,a,b) for(int i=a;i>=b;i--)
#define ll long long
const int MAXN = 2e5+5;
vector<int>v[MAXN],g;
int s,t,ans1,ans2;
int dep[MAXN],num[MAXN];
void dfs(int u,int fa){
int len=v[u].size();
rep(i,0,len-1){
int y=v[u][i];
if(y==fa)continue;
dep[y]=dep[u]+1;
dfs(y,u);
}
}
void dfs1(int u,int fa){
if(ans1!=-1)return;
g.push_back(u);
if(u==t){
int len=g.size();
if(len%2==0){
ans1=g[len/2-1];
ans2=g[len/2];
}
else{
ans1=g[len/2];
}
return;
}
int len=v[u].size();
rep(i,0,len-1){
int y=v[u][i];
if(y==fa)continue;
dfs1(y,u);
}
g.pop_back();
}
bool check1(int u,int fa){
if(fa==-1)memset(num,0,sizeof(num));
int len=v[u].size();
vector<int>p;
num[u]=1;
rep(i,0,len-1){
int y=v[u][i];
if(y==fa||y==ans1||y==ans2)continue;
if(!check1(y,u))return 0;
num[u]+=num[y];
p.push_back(y);
}
len=p.size();
if(len!=0&&len!=2){
return 0;
}
if(len==2){
if(num[p[0]]!=num[p[1]])return 0;
}
return 1;
}
bool check2(int u,int fa){
if(fa==-1)memset(num,0,sizeof(num));
int len=v[u].size();
vector<int>p;
num[u]=1;
rep(i,0,len-1){
int y=v[u][i];
if(y==fa)continue;
if(!check2(y,u))return 0;
num[u]+=num[y];
p.push_back(y);
}
len=p.size();
if(len!=0&&len!=2){
if(ans2!=-1)return 0;
ans2=u;
num[u]++;
}
if(len==2){
if(num[p[0]]!=num[p[1]])return 0;
}
return 1;
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
int n,a,b;
while(cin>>n){
n=(1<<n)-2;
rep(i,1,n)v[i].clear();
dep[1]=1;
rep(i,1,n-1){
cin>>a>>b;
v[a].push_back(b);
v[b].push_back(a);
}
dfs(1,-1);
s=0;
rep(i,1,n)if(dep[i]>dep[s])s=i;
t=0;
dfs(s,-1);
rep(i,1,n)if(dep[i]>dep[t])t=i;
ans1=ans2=-1;
g.clear();
dfs1(s,-1);
if(ans2!=-1){
if(check1(ans1,-1)&&check1(ans2,-1)){
cout<<2<<endl;
if(ans1>ans2)swap(ans1,ans2);
cout<<ans1<<" "<<ans2<<endl;
}
else{
cout<<0<<endl;
}
}
else{
if(check2(ans1,-1)){
cout<<1<<endl;
cout<<ans2<<endl;
}
else{
cout<<0<<endl;
}
}
}
}