[CF1228F](One Node is Gone)

题意

    定义一颗被删去一个节点的树为:原本有一个满二叉树,选择一个节点,断开它和父亲孩子的连边,然后该节点父亲与该节点的两个孩子分别连边

     现在给你一颗被树,要判断这棵树是不是被删去一个节点的树,如果是,求出有多少种可能被删去的节点及被删去的节点的父亲(有多种可能节点的话按编号排序输出)

solution

    background:

     一道syk眼中的签到水题树上乱搞好题

     可能是最近码力上来了水题做多了,居然没写挂

     solution:

     首先要遍历树,就得找到原来的根。显然原来的根在直径中点上,如果有两个中点就都要搜一遍。

     对于一个节点,我们记两个值 op 和 h,表示该点的子树内是否少了一个点和最大深度

     然后按照该点有0/1/2/3个孩子讨论一波就好了(以下省略awa)

     自己画图理解吧,当然代码里也有注释

code

    

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<vector>
#define N 200000
using namespace std;
struct zero{
    int nxt,to;
}edge[N<<1];
int head[N],tot=0;
void add_edge(int a,int b){edge[++tot]=(zero){head[a],b};head[a]=tot;}
int n,m,plk,poi,d1,d2,dep[N],kl[N],d[N],op[N],h[N];
bool flag;
void aux(int x,int fa){
  if(dep[x]>plk)plk=dep[x],poi=x;
  for(int i=head[x];i;i=edge[i].nxt){
      int to=edge[i].to;
      if(to==fa)continue;
      dep[to]=dep[x]+1;
      kl[to]=x;
      aux(to,x);
  }
}
bool cmp(int a,int b){return h[a]<h[b];}
void solve(int x,int fa){//这里flag=0就是GG(不可行)了
    vector<int> v;
    for(int i=head[x];i;i=edge[i].nxt){
        int to=edge[i].to;
        if(to==fa)continue;
        v.push_back(to);
    }
    op[x]=0;
    if(v.size()==0)h[x]=op[x]=0;//叶子节点,什么事都没有
    else if(v.size()==1){
        int to=v[0];
        solve(to,x);
        if(op[to]||h[to])flag=0;
        op[x]=x,h[x]=h[v[0]]+1;//一个孩子,那肯定少了一边,所以只有另一个孩子没有问题且少的孩子是叶子结点才不会GG
    }
    else if(v.size()==2){
        solve(v[0],x),solve(v[1],x);
        if((op[v[0]]&&op[v[1]])||h[v[0]]!=h[v[1]])flag=0;
        else if(op[v[0]]||op[v[1]])op[x]=op[v[0]]+op[v[1]];//两个孩子,该节点的op取决于孩子的op
        h[x]=h[v[0]]+1;
    }
    else if(v.size()==3){
        solve(v[0],x),solve(v[1],x),solve(v[2],x);
        sort(v.begin(),v.end(),cmp);
        if(op[v[0]]||op[v[1]]||op[v[2]])flag=0;
        if(!(h[v[1]]==h[v[0]]&&h[v[2]]==h[v[0]]+1))flag=0;
        op[x]=x,h[x]=h[v[2]]+1;//三个孩子,画个图就会发现只有一种情况不会GG
    }
    else flag=0;
}
int main(){
scanf("%d",&n);
m=(1<<n)-2;
for(int i=1;i<m;i++){
    int a,b;
    scanf("%d%d",&a,&b);
    add_edge(a,b),add_edge(b,a);d[a]++,d[b]++;
}
poi=plk=dep[1]=0;aux(1,0);d1=poi;
poi=plk=dep[d1]=kl[d1]=0;aux(d1,0);d2=poi;
int poss=d2;
vector<int> syk;
while(poss){
    if(abs(2*dep[poss]-dep[d2])<=1){
flag=1;solve(poss,0);//是直径中点
if(op[poss]&&flag&&h[poss]==n-1)syk.push_back(op[poss]);//注意这里还要判树的深度为n-1
} poss
=kl[poss]; } cout<<syk.size()<<endl; sort(syk.begin(),syk.end()); for(int i=0;i<syk.size();i++)cout<<syk[i]<<" "; }

 

posted @ 2019-09-30 13:36  stepsys  阅读(...)  评论(...编辑  收藏