Codeforces Round #589 (Div. 2) F. One Node is Gone (思维+模拟+dfs+满二叉树性质)

题目链接: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;
            }
        }
    }
}

猜你喜欢

转载自blog.csdn.net/toohandsomeIeaseId/article/details/104128408