Luogu

LOJ

分析

我们把每个节点都满足有一棵大小 $\leq 1$ 的子树的树称作「树枝」。

那么,$\mathrm{grow}(\mathscr{T})$ 是几乎完备的当且仅当所有高度为 $h$ 的树枝都在 $\mathrm{grow}(\mathscr{T})$ 中,这里的 $h$ 为 $\mathscr{T}$ 中树高的最大值。证明比较简单,因为任意一棵高度 $>h$ 的树都能由某个高度为 $h$ 的树枝生成,而一个高度为 $h$ 的树枝可以生成的无穷多棵树均不能被其它高度为 $h$ 的树枝生成。

这个定理实际上等价于 $\mathrm{grow}(\mathscr{T})$ 是几乎完备的当且仅当仅有有限个树枝不在 $\mathrm{grow}(\mathscr{T})$ 中。

考虑到树枝只能由树枝生成,所以我们可以去掉 $\mathscr{T}$ 中所有不是树枝的树。

设 $\mathrm{solve}(\mathscr{T})$ 表示 $\mathrm{grow}(\mathscr{T})$ 是否几乎完备。显然 $\mathscr{T}$ 中有一个单独节点时 $\mathrm{solve}{\mathscr{T}}=1$,否则我们把 $\mathscr{T}$ 中所有树分成四类:

  • 没有左儿子的,将其右子树集合记做 $\mathscr{A}$。
  • 没有右儿子的,将其左子树集合记做 $\mathscr{B}$。
  • 左子树大小为 $1$ 的,将其右子树集合记做 $\mathscr{C}$。
  • 右子树大小为 $1$ 的,将其左子树集合记做 $\mathscr{D}$。

那么如果 $\mathrm{solve}(\mathscr{A})$、$\mathrm{solve}(\mathscr{B})$、$\mathrm{solve}(\mathscr{C})$、$\mathrm{solve}(\mathscr{D})$ 都为真,则 $\mathrm{solve}{(\mathscr{T})}$ 为真。因为可能的树枝只能由这四种方式生成,如果有一种不几乎完备则可以构造出无数棵不在 $\mathrm{grow}(\mathscr{T})$ 中的树。

时间复杂度大概是 $\mathcal{O}(\sum n)$ 的。

代码

// ====================================
//   author: M_sea
//   website: https://m-sea-blog.com/
// ====================================
#include <bits/stdc++.h>
#define file(x) freopen(#x".in","r",stdin); freopen(#x".out","w",stdout)
using namespace std;
typedef long long ll;

int read() {
    int X=0,w=1; char c=getchar();
    while (c<'0'||c>'9') { if (c=='-') w=-1; c=getchar(); }
    while (c>='0'&&c<='9') X=X*10+c-'0',c=getchar();
    return X*w;
}

const int N=2000000+10;

int m,tot=0,ls[N],rs[N];
vector<int> t;

bool isleaf(int o) { return !ls[o]&&!rs[o]; }
bool solve(vector<int> t) {
    if (t.empty()) return 0;
    for (int o:t) if (isleaf(o)) return 1;
    vector<int> t1,t2,t3,t4;
    for (int o:t) {
        if (!ls[o]) t1.emplace_back(rs[o]);
        if (!rs[o]) t2.emplace_back(ls[o]);
        if (ls[o]&&rs[o]) {
            if (isleaf(ls[o])) t3.emplace_back(rs[o]);
            if (isleaf(rs[o])) t4.emplace_back(ls[o]);
        }
    }
    return solve(t1)&&solve(t2)&&solve(t3)&&solve(t4);
}

int main() {
    int T=read();
    while (T--) {
        t.clear(),tot=0;
        m=read();
        for (int i=1;i<=m;++i) {
            int n=read(); t.emplace_back(tot+1);
            for (int j=1;j<=n;++j) {
                ls[tot+j]=read(); if (ls[tot+j]) ls[tot+j]+=tot;
                rs[tot+j]=read(); if (rs[tot+j]) rs[tot+j]+=tot;
            }
            tot+=n;
        }
        puts(solve(t)?"Almost Complete":"No");
    }
    return 0;
}
最后修改:2020 年 08 月 24 日 07 : 04 PM