LOJ3343. 「NOI2020」超现实树
定义一棵二叉树“生长”得到的树为:将其的所有叶子结点替换任意二叉树,得到的树。
给出一堆树,现在问是否有限棵树不能被这些树中任意一个生长得到。
(题目表述为:几乎所有树都能被生长到)
\(\sum |T|\le 2*10^6\)
过了一年还不会做的神仙题。尽管是看了题解才会的,但还是尽量模拟一下正推的思路。
定义\(S\)能生长到\(T\)为\(T\)被\(S\)包含。严谨地表述一下,\(T\subseteq S\)当且仅当:\(T\)和\(S\)的非叶子节点的交集的左右儿子状态相同(是否有左儿子,是否有右儿子)。
题目相当于问是否能构造出一棵无限大的树,满足其不被任何一棵树包含。
考虑看是否生长出一个基,这个基满足:
- 几乎包含了所有树。
- 基中任何一棵树都不能生成另一棵树。
结合上面包含的定义(考虑左右儿子状态),尝试看看一个基包含了所有可能的左右儿子状态会怎么样。
然后快进到结论:
钦定基中的每棵树的高度为\(h\),有个深度\(1\)到\(h-1\)的主链。主链上的每个点的儿子中除了那个在主链上的点之外另一个儿子为空或者为单点。(题解说:每个节点存在一个儿子大小不超过\(1\),称这个东西为树枝)
可以发现,所有高度为\(h\)的树枝恰好组成了一个基。还有个很重要的是:非树枝不能生成树枝,也无法替代树枝的作用。于是给出的树中只需要丢掉非树枝,判断树枝能否几乎包含所有树即可。
具体实现:递归,如果存在单点就返回1;否则分别递归左儿子(无右儿子),递归右儿子(无左儿子),递归左儿子(右儿子大小为1),递归右儿子(左儿子大小为1),要求都返回1。把每棵树丢到它所属的位置,注意可能存在树同时属于后两者特判一下。
using namespace std;
#include <bits/stdc++.h>
#define fi first
#define se second
#define mp make_pair
int input() {
char ch = getchar();
while (ch < '0' || ch > '9')
ch = getchar();
int x = 0;
do {
x = x * 10 + ch - '0';
ch = getchar();
} while ('0' <= ch && ch <= '9');
return x;
}
const int N = 2000005;
int m;
vector<pair<int, int>> _t[N];
pair<int, int> _q[N], q_[N];
int dfs(pair<int, int> q[], int k) {
if (k == 0)
return 0;
int c[4] = {0, 0, 0, 0}, s[4]; //l0,l1,r0,r1
int _11 = 0;
for (int i = 1; i <= k; ++i) {
vector<pair<int, int>> &t = _t[q[i].fi];
int x = q[i].se;
if (t[x].fi == 0 && t[x].se == 0)
return 1;
if (t[x].fi && t[x].se == 0)
c[0]++;
else if (t[x].se && t[x].fi == 0)
c[2]++;
else if (t[x].fi && t[x].se) {
if (t[t[x].se].fi == 0 && t[t[x].se].se == 0 && t[t[x].fi].fi == 0 && t[t[x].fi].se == 0)
_11 = 1;
else if (t[t[x].se].fi == 0 && t[t[x].se].se == 0)
c[1]++;
else if (t[t[x].fi].fi == 0 && t[t[x].fi].se == 0)
c[3]++;
}
}
s[0] = c[0];
for (int i = 1; i <= 3; ++i)
s[i] = s[i - 1] + c[i];
for (int i = 1; i <= k; ++i) {
vector<pair<int, int>> &t = _t[q[i].fi];
int x = q[i].se;
if (t[x].fi && t[x].se == 0)
q_[s[0]--] = mp(q[i].fi, t[x].fi);
else if (t[x].se && t[x].fi == 0)
q_[s[2]--] = mp(q[i].fi, t[x].se);
else if (!_11 && t[x].fi && t[x].se) {
if (t[t[x].se].fi == 0 && t[t[x].se].se == 0)
q_[s[1]--] = mp(q[i].fi, t[x].fi);
if (t[t[x].fi].fi == 0 && t[t[x].fi].se == 0)
q_[s[3]--] = mp(q[i].fi, t[x].se);
}
}
memcpy(q + 1, q_ + 1, sizeof(pair<int, int>)*k);
return dfs(q, c[0]) && dfs(q + c[0] + c[1], c[2]) && (_11 || dfs(q + c[0], c[1]) &&
dfs(q + c[0] + c[1] + c[2], c[3]));
}
int main() {
// freopen("in.txt","r",stdin);
freopen("surreal.in", "r", stdin);
freopen("surreal.out", "w", stdout);
int T = input();
while (T--) {
m = input();
for (int i = 1; i <= m; ++i) {
int n = input();
_t[i].resize(n + 1);
for (int j = 1; j <= n; ++j) {
int x = input(), y = input();
//mp(input(),input())
_t[i][j] = mp(x, y);
//printf("%d %d\n",_t[i][j].fi,_t[i][j].se);
}
}
for (int i = 1; i <= m; ++i)
_q[i] = mp(i, 1);
int ans = dfs(_q, m);
if (ans)
printf("Almost Complete\n");
else
printf("No\n");
}
return 0;
}