[关闭]
@xiaoziyao 2020-11-21T15:15:21.000000Z 字数 1793 阅读 1054

P6776 [NOI2020]超现实树解题报告

解题报告


P6776 [NOI2020]超现实树解题报告:

更好的阅读体验

题意

分析

神仙题,同步赛的时候只想到了讨论儿子的情况,但是由于没想到只有链树可以对答案贡献,因此发现讨论不出来情况,最后只拿了

定义:我们定义链树为一条链上每个节点挂或不挂一个叶子结点形成的树

引理任意非链树的数无法通过替换得到链树

证明:替换操作不会删除任意原有的结点,因此新生成的树依旧无法满足链树的条件。

引理的完备性等价于只有有限棵链树不被包含

证明:

  • 充分性:如果无数棵链树不在中,那么肯定有无限棵树不在中;
  • 必要性:如果无数棵树不在中,那么我们一定有无限个深度不相同的树不在中,对于每一个深度的树我们都可以砍去一些子树使得这棵树变为链树,显然这棵链树也无法替换而成。(如果可以替换而成,那么一定可以生成这颗不包含在中的树)

引理只有链树可以对的完备性产生影响

证明:由于引理,我们知道任意一颗不是链树的树一定无法替换成链树,而由引理得我们只关心链树是否可以被替换而成,因此我们只需要链树。

这样,我们的任务就转化为了能否用给定的所有链树替换出无限的链树。(注意:下文中集合专指剔除了非链树的给定树集合)

首先,我们要判断链树(函数):

  1. int isleaf(int x){
  2. return x!=0&&ls[x]==0&&rs[x]==0;
  3. }
  4. int check(int x){
  5. if(x==0||isleaf(x))
  6. return 1;
  7. return (check(ls[x])==0&&check(rs[x])==0)? 0:1;
  8. }

对于链树上的结点,它儿子的情况只有四种:①只有左儿子②只有右儿子③有左右儿子,但是有一个儿子是叶子结点(根据链树的定义很显然有这一点)。

因此我们也可以很快写出来链树的合并:

  1. void merge(int &now,int x){
  2. if(now==0)
  3. now=++cnt;
  4. if(isleaf(x)){
  5. ok[now]=1;
  6. return ;
  7. }
  8. if(isleaf(ls[x])&&isleaf(rs[x])){
  9. merge(chd[now][2],ls[x]),merge(chd[now][3],rs[x]);
  10. return ;
  11. }
  12. if(ls[x]==0)
  13. merge(chd[now][1],rs[x]);
  14. if(rs[x]==0)
  15. merge(chd[now][0],ls[x]);
  16. if(rs[x]&&isleaf(ls[x]))
  17. merge(chd[now][3],rs[x]);
  18. if(ls[x]&&isleaf(rs[x]))
  19. merge(chd[now][2],ls[x]);
  20. }

定义结点为好点当且仅当只存在有限棵树不能被的子树替换出来,好点的判断可以通过一种递归的方式写出:

  1. int grow(int x){
  2. if(x==0)//不存在这个点
  3. return 0;
  4. if(ok[x]==1)//解释见下
  5. return 1;
  6. return grow(chd[x][0])&&grow(chd[x][1])&&grow(chd[x][2])&&grow(chd[x][3]);
  7. //chd[x][0/1/2/3]为x儿子的四种情况
  8. }

解释:某个子树可以替换出来它子树所有情况只有两种情况:要么是给定的链树集合存在一棵树让这个位置的结点为叶子结点(这样一定可以替换出所有的树),要么是这个点所有的儿子都是好点。

这样我们只需要判断根结点是否为好点就可以了。

代码

代码&压行后的代码

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注