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;
    }
    
  • 相关阅读:
    【Java EE 学习 81】【CXF框架】【CXF整合Spring】
    【Java EE 学习 80 下】【调用WebService服务的四种方式】【WebService中的注解】
    【Java EE 学习 80 上】【WebService】
    【Java EE 学习 79 下】【动态SQL】【mybatis和spring的整合】
    【Java EE 学习 79 上】【mybatis 基本使用方法】
    【Java EE 学习 78 下】【数据采集系统第十天】【数据采集系统完成】
    【Java EE 学习 78 中】【数据采集系统第十天】【Spring远程调用】
    【Java EE 学习 78 上】【数据采集系统第十天】【Service使用Spring缓存模块】
    【Java EE 学习 77 下】【数据采集系统第九天】【使用spring实现答案水平分库】【未解决问题:分库查询问题】
    【Java EE 学习 77 上】【数据采集系统第九天】【通过AOP实现日志管理】【通过Spring石英调度动态生成日志表】【日志分表和查询】
  • 原文地址:https://www.cnblogs.com/Flying2018/p/13537880.html
Copyright © 2011-2022 走看看