P6776 [NOI2020] 超现实树 题解
P6776 [NOI2020] 超现实树 题解
非常好的性质结论题,通过这道题你可以学习到:
-
对于一道性质题(给了一堆新定义,肯定是性质题),我应该如何思考。
-
如何缩小目标状态集合,从而增加性质,减少情况。
-
如何缩小已达状态集合,从而增加性质,减少情况。
那么开始吧。
一、题解题意
首先明确题意:给你一些二叉树问你能不能通过这些树「生长」(二叉树替换叶子)得到几乎所有树。
- 「几乎所有」 = 只有有限棵树得不到。
二、从小例子找规律
例子1:如果给你一棵「单点树」,一定是可以生成所有树的。
例子2:给你两棵树:「左链树」和「右链树」,不能生成几乎所有的树。
通过一些例子,我们至少发现两个事实:
- 越简单的树生成能力越强。
- 深度越小的树生成能力越强
于是可以考虑一个理想情况,不管题目原有的已有的树,对已有深度进行限制 \(h\ge w\),然后找出一类「最小生成单元」的树,满足:
-
它们能通过替换生成其他树。
-
如果 能生成这些「基础树」,就能生成几乎所有树(充分性),否则不能几乎生成所有的树(必要性)。
玩亿下,可以发现,不论 \(w\) 取多少,这样的「最小生成单元」一定存在,称为「基础树」,结合分析,它们应该满足以下条件之一:
对于树的每个节点:
-
单点。
-
仅左儿子和仅右儿子。
-
双儿子,其中至少有一个是「单点树」。
因为选择总是简单的(空或者单点),这三条满足了”越简单的树生成能力越强“的性质,是对的。
三、总结并缩小目标状态集合
性质1:一旦当且仅当只有有限多个好「基础树」不在 \(grow(T)\) 中,树林 \(T\) 是几乎完备的。
证明考虑,因为任何一棵树 T 可以由深度相等的一棵好树长出,所以只要一定深度以上的好树都能被生成,那么这个深度以上的所有树都能被生成。
四、总结并缩小已达状态集合
目标状态后,考虑哪些已达状态是没用的,发现为:如果输入的一棵树不是「基础树」,那么它便无用。
证明考虑,这棵树不能再生成任何的「基础树」了。
所以已达树也一定是「基础树」,简化了集合,暴力递归判断即可,由于一棵树只有的每个节点只会递归到一边判断一次,复杂度 \(\mathcal O(\sum n)\)。
五、实现代码:
#include<bits/stdc++.h>
#define io ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
#define file(s) freopen(s".in","r",stdin),freopen(s".out","w",stdout)
#define int long long
#define double long double
#define loop(i,a,b) for(int i=a;i<=b;i++)
#define pool(i,a,b) for(int i=a;i>=b;i--)
#define mset(a,v) memset(a,v,sizeof a)
#define mcpy(a,b) memcpy(a,b,sizeof b)
#define myas(x) if(!(x))exit(1);
#define umap unordered_map
#define pb push_back
#define pc(x) __builtin_popcountll(x)
#define fi first
#define se second
using namespace std;
typedef unsigned long long ull;
typedef pair<int,int> pa;
typedef vector<int> vi;
#define DEBUG
#ifndef DEBUG
#define cerr none
ofstream none("nul");
#endif
const int NN=2e6+5;
int n,m,tot,ls[NN],rs[NN],siz[NN];
vector<int> gootr;
bool Check(int p){
if(!p)return 1;
siz[p]=1;
bool res=Check(ls[p])&&Check(rs[p]);
siz[p]+=siz[ls[p]]+siz[rs[p]];
return res&&min(siz[ls[p]],siz[rs[p]])<=1;
}
bool DFS(vi v){
vi v1,v2,v3,v4;
for(int o:v){
if(!ls[o]&&!rs[o])return true;
if(ls[o]&&!rs[o])v1.pb(ls[o]);
if(!ls[o]&&rs[o])v2.pb(rs[o]);
if(ls[o]&&rs[o]&&siz[ls[o]]==1)v3.pb(rs[o]);
if(ls[o]&&rs[o]&&siz[rs[o]]==1)v4.pb(ls[o]);//这里加入的树可能与上一行重复,不过如果有重复,下一层必定直接回溯
}
if(min({v1.size(),v2.size(),v3.size(),v4.size()})==0)return false;
return DFS(v1)&&DFS(v2)&&DFS(v3)&&DFS(v4);
}
void Work(){
tot=0;
cin>>m;
gootr.clear();
loop(i,1,m){
cin>>n;
loop(j,tot+1,tot+n){
cin>>ls[j]>>rs[j];
if(ls[j])ls[j]+=tot;
if(rs[j])rs[j]+=tot;
}
if(Check(tot+1))gootr.pb(tot+1);
tot+=n;
}
DFS(gootr)?cout<<"Almost Complete\n":cout<<"No\n";
return;
}
signed main(){
io;int T;cin>>T;
while(T--)Work();
return 0;
}

浙公网安备 33010602011771号