zoukankan      html  css  js  c++  java
  • [NOI2020] 超现实树

    链接

    为什么我没有看到第5,6个样例?

    题目大意

    定义一颗二叉树 (S) 将若干个叶子节点替换为任意二叉树后的树与 (T) 同构,称为 (S) 包含 (T)

    现在给定 (n) 颗树 (S_i),问对于所有二叉树,是否只有有限个二叉树没有任意一个 (S_i) 包含。

    题解

    结论题。

    首先我们可以把题目看成:问能否构造一颗高度无限的二叉树,使其不被任意一个 (S_i) 包含。

    考虑包含的性质,我们可以再次转换问题,即:问能否构造一颗无限长的二叉树,使对于任意 (S_i) ,存在一个非叶子节点的儿子信息不同(有/无左儿子,有/无右儿子)。

    再考虑一个很显然的性质:如果有两棵二叉树 (S,T)(T) 根的左子树不被 (S) 根的左子树包含,那么 (T) 一定不被 (S) 包含。

    首先,如果存在 (S_i) 是一个节点,那么一定不存在。

    否则贪心地从根考虑构造:可以发现,如果根的左子树中存在一种树,不被任何 (S_i) 根的左子树包含,那么就存在一颗无限长的二叉树,使其不被任意一个 (S_i) 包含。

    即我们希望在左子树中构造一种树,使其不被当前集合中任何 (S_i) 包含。如果成功,由于上述性质,意味着右子树无论怎么构造都不会被包含。

    很明显,假如这种树存在,其中一定包含只有根结点的树。所以构造只有根结点的树(即叶子)一定不会劣。

    反之,如果左子树中不存在这样的一种树,即当前的 (S_i) 中存在一棵树,其根的左儿子是叶子。那么这样作为构造方就有了一种选择:选/不选左儿子。可以发现第一条对应的集合变成了“根有左儿子且左儿子是叶子”,第二条对应的集合变成了“根没有左儿子”。

    对右儿子同理。可以发现,作为构造方,我们现在有4种选择:

    1. 空出左子树,递归构造右子树。
    2. 空出右子树,递归构造左子树。
    3. 左子树放叶子,递归构造右子树。
    4. 右子树放叶子,递归构造右子树。

    可以发现,这样把集合 (S_i) 分成了4份。对于每份集合,必然存在无限种构造使该树不被其他集合的树包含。

    对于4种情况,递归处理即可。

    特别的,对于左右子树都是叶子的树 (S),意味着构造3,4都会被 (S) 包含,所以这种情况下只需要递归构造1,2即可。

    理论上好像可以被卡到 (O(nsqrt n)),但实际跑得飞快。

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<vector>
    #define N 3000010
    using namespace std;
    vector<int> ls[N],rs[N];
    struct node{
        int t,u;
    };
    vector<node>id;
    inline bool lev(node a){return !ls[a.t][a.u] && !rs[a.t][a.u];}
    inline bool have_ls(node a){return ls[a.t][a.u];}
    inline bool have_rs(node a){return rs[a.t][a.u];}
    inline node lson(node a){return (node){a.t,ls[a.t][a.u]};}
    inline node rson(node a){return (node){a.t,rs[a.t][a.u]};}
    bool solve(vector<node> &s)
    {
        if(s.empty()) return false;
        for(node i:s) if(lev(i)) return true;
        vector<node>s1,s2,s3,s4;
        bool can=false;
        for(node i:s)
        if(!have_ls(i)) s1.push_back(rson(i));
        else if(!have_rs(i)) s2.push_back(lson(i));
        else if(lev(lson(i)) && lev(rson(i))) can=true;
        else if(lev(lson(i))) s3.push_back(rson(i));
        else if(lev(rson(i))) s4.push_back(lson(i));
        s.clear();
        if(s1.empty() || s2.empty() || (!can && (s3.empty() || s4.empty()))) return false;
        return solve(s1) && solve(s2) && (can || (solve(s3) && solve(s4)));
    }
    int main()
    {
        freopen("surreal.in","r",stdin);
        freopen("surreal.out","w",stdout);
        int t;
        scanf("%d",&t);
        while(t --> 0)
        {
            int n;
            scanf("%d",&n);
            id.resize(n);
            for(int i=0;i<n;i++)
            {
                int tn;
                scanf("%d",&tn);
                id[i]=(node){i,1};
                ls[i].resize(tn+1),rs[i].resize(tn+1);
                for(int j=1;j<=tn;j++) scanf("%d%d",&ls[i][j],&rs[i][j]);
            }
            puts(solve(id)?"Almost Complete":"No");
        }
        return 0;
    }
    
  • 相关阅读:
    Haskell Interactive Development in Emacs
    Access Java API in Groovy Script
    手工设置Eclipse文本编辑器的配色
    Color Theme of Emacs
    Gnucash的投资记录
    Special Forms and Syntax Sugars in Clojure
    Use w3m as Web Browser
    SSE指令集加速之 I420转BGR24
    【图像处理】 增加程序速度的方法
    TBB 入门笔记
  • 原文地址:https://www.cnblogs.com/Flying2018/p/13537880.html
Copyright © 2011-2022 走看看