Loading

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;
}

参考&鸣谢:

题解 P6776 【[NOI2020]超现实树】 - 洛谷专栏

某科学的超现实树 - 洛谷专栏

DeepSeek | 深度求索

posted @ 2025-06-12 20:50  lupengheyyds  阅读(29)  评论(0)    收藏  举报