@xiaoziyao
2020-11-21T23:15:21.000000Z
字数 1793
阅读 1272
解题报告
P6776 [NOI2020]超现实树解题报告:
神仙题,同步赛的时候只想到了讨论儿子的情况,但是由于没想到只有链树可以对答案贡献,因此发现讨论不出来情况,最后只拿了。
定义:我们定义链树为一条链上每个节点挂或不挂一个叶子结点形成的树。
引理:任意非链树的数无法通过替换得到链树
证明:替换操作不会删除任意原有的结点,因此新生成的树依旧无法满足链树的条件。
引理:的完备性等价于只有有限棵链树不被包含。
证明:
- 充分性:如果无数棵链树不在中,那么肯定有无限棵树不在中;
- 必要性:如果无数棵树不在中,那么我们一定有无限个深度不相同的树不在中,对于每一个深度的树我们都可以砍去一些子树使得这棵树变为链树,显然这棵链树也无法替换而成。(如果可以替换而成,那么一定可以生成这颗不包含在中的树)
引理:只有链树可以对的完备性产生影响。
证明:由于引理,我们知道任意一颗不是链树的树一定无法替换成链树,而由引理得我们只关心链树是否可以被替换而成,因此我们只需要链树。
这样,我们的任务就转化为了能否用给定的所有链树替换出无限的链树。(注意:下文中集合专指剔除了非链树的给定树集合)
首先,我们要判断链树(函数):
int isleaf(int x){
return x!=0&&ls[x]==0&&rs[x]==0;
}
int check(int x){
if(x==0||isleaf(x))
return 1;
return (check(ls[x])==0&&check(rs[x])==0)? 0:1;
}
对于链树上的结点,它儿子的情况只有四种:①只有左儿子②只有右儿子③有左右儿子,但是有一个儿子是叶子结点(根据链树的定义很显然有这一点)。
因此我们也可以很快写出来链树的合并:
void merge(int &now,int x){
if(now==0)
now=++cnt;
if(isleaf(x)){
ok[now]=1;
return ;
}
if(isleaf(ls[x])&&isleaf(rs[x])){
merge(chd[now][2],ls[x]),merge(chd[now][3],rs[x]);
return ;
}
if(ls[x]==0)
merge(chd[now][1],rs[x]);
if(rs[x]==0)
merge(chd[now][0],ls[x]);
if(rs[x]&&isleaf(ls[x]))
merge(chd[now][3],rs[x]);
if(ls[x]&&isleaf(rs[x]))
merge(chd[now][2],ls[x]);
}
定义结点为好点当且仅当只存在有限棵树不能被的子树替换出来,好点的判断可以通过一种递归的方式写出:
int grow(int x){
if(x==0)//不存在这个点
return 0;
if(ok[x]==1)//解释见下
return 1;
return grow(chd[x][0])&&grow(chd[x][1])&&grow(chd[x][2])&&grow(chd[x][3]);
//chd[x][0/1/2/3]为x儿子的四种情况
}
解释:某个子树可以替换出来它子树所有情况只有两种情况:要么是给定的链树集合存在一棵树让这个位置的结点为叶子结点(这样一定可以替换出所有的树),要么是这个点所有的儿子都是好点。
这样我们只需要判断根结点是否为好点就可以了。