Solution -「NOI 2020」「洛谷 P6776」超现实树
\(\mathcal{Description}\)
Link.
对于非空二叉树 \(T\),定义 \(\operatorname{grow}(T)\) 为所有能通过若干次“替换 \(T\) 的某个叶子为任意非空二叉树”的操作得到的二叉树集合;对于非空二叉树集合 \(\mathscr T\),定义 \(\operatorname{grow}(\mathscr T)=\bigcup_{T\in{\mathscr T}}\operatorname{grow}(T)\)。多次询问,每次给定一个 \(\mathscr T\),询问是否仅有有限个二叉树不在 \(\operatorname{grow}(\mathscr T)\) 中。二叉树判等时区分左右儿子,点无编号。
设单棵树的结点数为 \(n\),则 \(\sum n\le2\times10^6\)。
这 tm 才叫简要题意。
\(\mathcal{Solution}\)
谨以本题解表达去年做题的兔子对题面的赞美。
\(\mathbf{X1}\):我们先来研究一个几乎完备的 \(\operatorname{grow}(\mathscr T)\) 吧,在这样的 \(\mathscr T\) 中,会不会有些树不可起到使 \(\operatorname{grow}(\mathscr T)\) 完备的作用呢?
\(\mathbf{X2}\):嗯,每片叶子都可以生长出任何二叉树,那么,起到决定性作用的是否叶子们的深度?
\(\mathbf{X1}\):那就先控制深度信息!我发现书上也有在这个思路下衍生的定义。
定义:二叉树集合 \(\mathscr B_n\) 为高度为 \(n\) 的全体二叉树集的基,定义为:
\[\mathscr B_n=\{T~|~h(T)=n\land(~\not\exist T',~h(T')=n\land T\in\operatorname{grow}(T'))\}
\]
\(\mathbf{X2}\):简而言之,这个 \(\mathscr B_n\) 包含了所有高度为 \(n\) 且不能被其他高度为 \(n\) 的树“生长”出来。很显然,\(\operatorname{grow}(\mathscr B_n)\) 包含了所有高度为 \(n\) 的二叉树!
\(\mathbf{X1}\):继续深入的话……说不定 \(\mathscr B_n\) 里的 \(T\) 也有特殊性质呢。
\(\mathbf{X2}\):(手玩片刻)呀!这样的 \(T\) 看上去都很“瘦小”,具体地,如果结点 \(u\) 同时拥有左右儿子,那么必然有至少一个儿子是叶子。嗯!这是能够证明的结论。
证明:记 \(p(T)\) 为真时,\(T\) 满足上述性质,否则则不满足。设 \(h(T)=n\),我们希望证明
\[p(T)\Leftrightarrow T\in\mathscr B_n
\]对于 \(p(T)\Rightarrow T\in\mathscr B_n\),取出 \(T\) 中一定存在的自根而下长为 \(n\) 的树链 \(L\),这样的链是单独的一条或者在末尾分叉的两条。我们可以断言,若有 \(h(T')=n,T\in\operatorname{grow}(T')\),则 \(T'\) 必然满足 \(L\sube T'\)。而对于其余叶子,由于它们的父亲都在 \(T'\) 的点集中且不是叶子,所以它们也必然在 \(T'\) 的点集中。最终,发现 \(T=T'\),故命题成立。
另一边,\(p(T)\Leftarrow T\in\mathscr B_n\),仍使用反证法。对于不满足条件的 \(T\),取出一个左右儿子存在且均不是叶子的结点 \(u\),它必然有一个儿子 \(v\) 满足删去 \(v\) 子树后 \(h(T)=n\)。则可将 \(v\) 子树“逆向生长”为单点 \(v\),此时得到的 \(T'\) 满足 \(h(T)=n\) 且 \(T\in\operatorname{grow}(T')\),所以 \(T\not\in\mathscr B_n\),假设矛盾,原命题成立。
综上,命题得证。
\(\mathbf{X1}\):离成功更近一步!把这个强大的结论用于题目本身,是不是说,如果 \(T\in\mathscr T\) 不满足上述条件,就能从 \(\mathscr T\) 中剔除 \(T\) 而不改变 \(\operatorname{grow}(\mathscr T)\) 的完备性?……没错!如果 \(T\) 是这样一棵树,且不存在 \(T'\in\mathscr T\setminus\{T\}\) 使得 \(T\in\operatorname{grow}(T')\),如果存在某棵树只能由 \(T\) 生长出来,我们一定能找到这棵树中一个左右儿子均存在且不是叶子的结点 \(u\),将它的一个儿子替换为单点。那么,这棵树的 \(\operatorname{grow}\) 集合与 \(\operatorname{grow}(\mathscr T)\) 交集为空!
\(\mathbf{X2}\):接下来就只需要直面问题,尝试判断 \(\operatorname{grow}(\mathscr T)\) 是否几乎完备。我们从一棵“希望与 \(\operatorname{grow}(\mathscr T)\) 匹配的树”的视角出发,设当前结点为 \(u\),如果 \(u\) 子树能够匹配上:
- 需要 \(T\in\mathscr T\),\(T\) 在此位置上的结点是叶子,这强于以下所有条件。
- \(u\) 只有左儿子,需要一个只有左儿子的 \(T\);
- \(u\) 只有右儿子,需要一个只有右儿子的 \(T\);
- \(u\) 左儿子是叶子,需要同样情况的 \(T\);
- \(u\) 右儿子是叶子,需要同样情况的 \(T\);
- \(u\) 左右儿子都是叶子,3. 4. 任意。
我们定义 \(\operatorname{syno}(\mathscr T)\) 是一棵四叉树,它是对 \(\mathscr T\) 的等价概括。我们可以每个 \(T\) 合并入其中,依照 1. 2. 3. 4. 四种情况划分儿子编号。最后,定义 \(f(\operatorname{syno}(\mathscr T),u)\) 该四叉树中 \(u\) 子树的完备性,有
\text{False}&u\text{ is not in }\operatorname{syno}(\mathscr T)\\
\text{True}&u\text{ meets condition 0.}\\
\bigwedge_{i=0}^3f(\operatorname{syno}(\mathscr T),\operatorname{son}(u,i))&\text{otherwise}
\end{cases}
\]
最终,\(f(\operatorname{syno}(\mathscr T),\operatorname{root})\) 即为答案。复杂度 \(\mathcal O(\sum|T|)\)!
\(\mathbf{X1}\):不过我发现一个问题欸,为什么题目描述比题解还长呢?
\(\mathcal{Code}\)
/* Clearink */
#include <cstdio>
#include <cassert>
#define rep( i, l, r ) for ( int i = l, rep##i = r; i <= rep##i; ++i )
#define per( i, r, l ) for ( int i = r, per##i = l; i >= per##i; --i )
inline char fgc() {
static char buf[1 << 17], *p = buf, *q = buf;
return p == q && ( q = buf + fread( p = buf, 1, 1 << 17, stdin ), p == q )
? EOF : *p++;
}
inline int rint() {
int x = 0; char s = fgc();
for ( ; s < '0' || '9' < s; s = fgc() );
for ( ; '0' <= s && s <= '9'; s = fgc() ) x = x * 10 + ( s ^ '0' );
return x;
}
const int MAXN = 2e6;
int m;
struct BinaryTree {
int node, ch[MAXN + 5][2];
inline void clear() {
rep ( i, 1, node ) ch[i][0] = ch[i][1] = 0;
node = 0;
}
inline void read() {
clear(), node = rint();
rep ( i, 1, node ) ch[i][0] = rint(), ch[i][1] = rint();
}
inline int type( const int u ) const {
/*
-1: illegal node (not a basic tree).
0: left only.
1: right only.
2: left chain and right leaf.
3: right chain and left leaf.
4: 2&3.
*/
int lc = ch[u][0], rc = ch[u][1];
assert( lc || rc );
if ( !rc ) return 0;
if ( !lc ) return 1;
bool lf = !ch[lc][0] && !ch[lc][1], rf = !ch[rc][0] && !ch[rc][1];
if ( lf && rf ) return 4;
if ( rf ) return 2;
if ( lf ) return 3;
return -1;
}
inline bool check( const int u ) {
if ( !u || ( !ch[u][0] && !ch[u][1] ) ) return true;
return ~type( u ) && check( ch[u][0] ) && check( ch[u][1] );
}
} binT;
struct SynopsisTree {
int node, ch[MAXN + 5][4];
bool tag[MAXN + 5];
inline void clear() {
rep ( i, 1, node ) {
ch[i][0] = ch[i][1] = ch[i][2] = ch[i][3] = 0,
tag[i] = false;
}
node = 1;
}
inline void storage( const BinaryTree& T, int& u, const int v ) {
if ( !v ) return ;
if ( !u ) u = ++node;
if ( tag[u] ) return ;
if ( !T.ch[v][0] && !T.ch[v][1] ) return void( tag[u] = true );
int f = T.type( v );
if ( !f ) storage( T, ch[u][0], T.ch[v][0] );
else if ( f == 1 ) storage( T, ch[u][1], T.ch[v][1] );
else {
if ( f == 2 || f == 4 ) storage( T, ch[u][2], T.ch[v][0] );
if ( f == 3 || f == 4 ) storage( T, ch[u][3], T.ch[v][1] );
}
}
inline bool complete( const int u ) {
if ( !u || tag[u] ) return !!u;
return complete( ch[u][0] ) && complete( ch[u][1] )
&& complete( ch[u][2] ) && complete( ch[u][3] );
}
} synT;
int main() {
for ( int T = rint(), root = 1; T--; ) {
synT.clear(), m = rint();
rep ( i, 1, m ) {
binT.read();
if ( binT.check( 1 ) ) synT.storage( binT, root, 1 );
}
puts( synT.complete( 1 ) ? "Almost Complete" : "No" );
}
return 0;
}
Solution -「NOI 2020」「洛谷 P6776」超现实树的更多相关文章
- 洛谷 P6776 - [NOI2020] 超现实树(找性质,神仙题)
洛谷题面传送门 nb tea 一道! 首先考虑怎样入手分析这个看似非常不可做的问题.首先题目涉及高度无穷的树,根本枚举不了.不过我们冷静一下就会发现,如果我们记 \(mx=\max\limits_{i ...
- 「区间DP」「洛谷P1043」数字游戏
「洛谷P1043」数字游戏 日后再写 代码 /*#!/bin/sh dir=$GEDIT_CURRENT_DOCUMENT_DIR name=$GEDIT_CURRENT_DOCUMENT_NAME ...
- 洛谷P3373 [模板]线段树 2(区间增减.乘 区间求和)
To 洛谷.3373 [模板]线段树2 题目描述 如题,已知一个数列,你需要进行下面两种操作: 1.将某区间每一个数加上x 2.将某区间每一个数乘上x 3.求出某区间每一个数的和 输入输出格式 输入格 ...
- 【BZOJ2830/洛谷3830】随机树(动态规划)
[BZOJ2830/洛谷3830]随机树(动态规划) 题面 洛谷 题解 先考虑第一问. 第一问的答案显然就是所有情况下所有点的深度的平均数. 考虑新加入的两个点,一定会删去某个叶子,然后新加入两个深度 ...
- Solution -「JSOI 2019」「洛谷 P5334」节日庆典
\(\mathscr{Description}\) Link. 给定字符串 \(S\),求 \(S\) 的每个前缀的最小表示法起始下标(若有多个,取最小的). \(|S|\le3\time ...
- Solution -「洛谷 P4372」Out of Sorts P
\(\mathcal{Description}\) OurOJ & 洛谷 P4372(几乎一致) 设计一个排序算法,设现在对 \(\{a_n\}\) 中 \([l,r]\) 内的元素排 ...
- Solution -「POI 2010」「洛谷 P3511」MOS-Bridges
\(\mathcal{Description}\) Link.(洛谷上这翻译真的一言难尽呐. 给定一个 \(n\) 个点 \(m\) 条边的无向图,一条边 \((u,v,a,b)\) 表示从 ...
- Solution -「APIO 2016」「洛谷 P3643」划艇
\(\mathcal{Description}\) Link & 双倍经验. 给定 \(n\) 个区间 \([a_i,b_i)\)(注意原题是闭区间,这里只为方便后文描述),求 \(\ ...
- Solution -「SDOI 2018」「洛谷 P4606」战略游戏
\(\mathcal{Description}\) Link. 给定一个 \(n\) 个点 \(m\) 条边的无向连通图,\(q\) 次询问,每次给出一个点集 \(s\),求至少在原图中删去多 ...
随机推荐
- Hive的分析函数的使用
原文: https://www.toutiao.com/i6769120000578945544/?group_id=6769120000578945544 我们先准备数据库.表和数据 开窗分析函数相 ...
- 离线下载第三方Python包
1.进入Python第三方包下载地(https://pypi.org/)搜索自己需要的包 2.下载需要的包的版本 3.将.whl格式的文件更改为.zip文件,并且解压 4.将解压的2个文件放到Pyth ...
- IDEA导入Web项目配置Tomcat启动
1.导入项目 2.配置project 3.导入项目模块 配置Models 4.配置Libraries 5. 6. 7.配置tomcat
- mysql数据库优化1
目录 数据库结构的设计优化 1.数据库结构的设计 2.针对大型的数据量提前进行分库和分表 3.分库分表带来的问题 4.表结构设计注意的问题 查询优化 1.查询语句的注意事项 2.应尽量避免在 wher ...
- JavaScript 中BOM的常用操作
JavaScript BOM操作 1.获取浏览器窗口尺寸 var width=window,innerWidth //获取可视窗口宽度 var height=window.innerHeight // ...
- java实现excel表格导入数据库表
导入excel就是一个上传excel文件,然后获取excel文件数据,然后处理数据并插入到数据库的过程 一.上传excel 前端jsp页面,我的是index.jsp 在页面中我自己加入了一个下载上传文 ...
- golang gin框架中使用protocol buffers和JSON两种协议
首先,我使用protobuf作为IDL,然后提供HTTP POST + JSON BODY的方式来发送请求. 能不能使用HTTTP POST + PB序列化后的二进制BODY呢? 做了一下尝试,非常简 ...
- 1000粉!使用Three.js制作一个专属3D奖牌🥇
背景 破防了 !突然发现 SegmentFault 平台的粉丝数量已经突破 1000 了,它是我的三个博客平台掘金.博客园.SegmentFault中首个粉丝突破 1000 的,于是设计开发这个页面, ...
- 计算机视觉3-> yolov5目标检测1 |从入门到出土
本来就想着是对自己第一次跑yolov5的coco128的一个记录,没想到现在准备总结一下的时候,一方面是继续学习了一些,另一方面是学长的一些任务的要求,挖出了更多的东西,所以把名字改为了"从 ...
- Vue.js之计算属性(computed)、属性监听(watch)与方法选项(methods)
vue.js官网:https://cn.vuejs.org/v2/guide/components-registration.html 一.计算属性-computed 1. 作用:能够避免数据冗余,通 ...