题意
定义一颗被删去一个节点的树为:原本有一个满二叉树,选择一个节点,断开它和父亲孩子的连边,然后该节点父亲与该节点的两个孩子分别连边
现在给你一颗被树,要判断这棵树是不是被删去一个节点的树,如果是,求出有多少种可能被删去的节点及被删去的节点的父亲(有多种可能节点的话按编号排序输出)
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]<<" "; }