[LOJ#2324]「清华集训 2017」小Y和二叉树
[LOJ#2324]「清华集训 2017」小Y和二叉树
试题描述
小Y是一个心灵手巧的OIer,她有许多二叉树模型。
小Y的二叉树模型中,每个结点都具有一个编号,小Y把她最喜欢的一个二叉树模型挂在了墙上,树根在最上面,左右子树分别在树根的左下方与右下方,且他们也都满足这样的悬挂规则。为了让这个模型更加美观,小Y选择了一种让这棵二叉树的中序遍历序列最小的悬挂方法。所谓中序遍历最小,就是指中序遍历的结点编号序列的字典序最小。
一天,这个模型不小心被掉在了地上,幸运的是,所有结点和边都没摔坏,但是她想不起这个模型原来是怎么悬挂的了,也就是说:她想不起来树根节点的编号了。
小Y最近忙于准备清华集训,所以没太多时间处理别的事情,她只好找到同样心灵手巧的你帮忙复原她的二叉树模型。
给定小Y的二叉树模型,结点的编号为 \(1\) ~ \(n\) ,你需要给出其可能的最小的中序遍历,方便小Y更快的摆好她的模型。
输入
第一行为一个正整数 \(n\) ,表示点的个数。
后接 \(n\) 行,每行若干个整数:
第 \(i+1\) 行的第一个整数为 \(k_i\),表示编号为 \(i\) 的结点的度数,后接 \(k_i\) 个整数 \(a_{i,j}\),表示编号为 \(i\) 的结点与编号为 \(a_{i,j}\) 的结点之间有一条边。
同一行输入的相邻两个元素之间,用恰好一个空格隔开。
输出
输出共一行, \(n\) 个整数,表示字典序最小的中序遍历。
输入示例
4
3 2 3 4
1 1
1 1
1 1
输出示例
2 1 3 4
数据规模及约定
对于 \(100\%\) 的数据,\(1 \le n \le 1000000, 1 \le k_i \le 3\)。
题解
首先我们可以 dp 出每个节点为根时所能得到的中序遍历最小的第一位。这个就是先随便选一个度数 \(<3\) 的当根,然后正反 dp 一下。
令 \(g_i\) 表示以 \(i\) 为根时最小的中序遍历的第一位(若 \(i\) 度数为 \(3\),则 \(g_i\) 无意义)。现在可以确定最后答案的第一位一定是 \(min\{g_i\}\),令 \(r = min\{g_i\}\),那么我们现在以 \(r\) 为根,求一下 \(f_i\)(即以 \(i\) 为根的子树的中序遍历的最小的第一位),可以发现可以把 \(r\) “看成”根,在填完 \(r\) 的时候,选择一个拥有较小 \(f_i\) 的儿子 \(i\) 递归(在原树中就是把这个儿子甩到右儿子的位置,另一个儿子甩到父亲的位置:即 \(左 \rightarrow 根 \rightarrow 右\) 变成了 \(右 \rightarrow 根 \rightarrow 父节点\),虽然树的形态改变,但中序遍历本身没有变),然后把另一个儿子接着“看成根”,再递归(注意还有如“只有一个儿子”等特殊情况,注意特判)……以此类推直至遍历完所有节点。
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cctype>
#include <algorithm>
using namespace std;
#define rep(i, s, t) for(int i = (s); i <= (t); i++)
#define dwn(i, s, t) for(int i = (s); i >= (t); i--)
int read() {
int x = 0, f = 1; char c = getchar();
while(!isdigit(c)){ if(c == '-') f = -1; c = getchar(); }
while(isdigit(c)){ x = x * 10 + c - '0'; c = getchar(); }
return x * f;
}
#define maxn 1000010
#define maxm 2000010
int n, deg[maxn], m, head[maxn], nxt[maxm], to[maxm];
void AddEdge(int a, int b) {
to[++m] = b; nxt[m] = head[a]; head[a] = m;
return ;
}
int f[maxn], g[maxn];
void dp1(int u, int fa) {
f[u] = (fa && deg[u] < 3) ? u : n + 1;
for(int e = head[u]; e; e = nxt[e]) if(to[e] != fa) dp1(to[e], u), f[u] = min(f[u], f[to[e]]);
return ;
}
void dp2(int u, int fa) {
if(fa) g[u] = min(g[fa], deg[fa] < 3 ? fa : n + 1);
else g[u] = n + 1;
int ls = -1, rs = -1;
for(int e = head[u]; e; e = nxt[e]) if(to[e] != fa) {
if(ls < 0) ls = to[e]; else rs = to[e];
}
if(ls < 0 && rs < 0) return ;
if(rs < 0) {
dp2(ls, u);
g[u] = min(g[u], f[ls]);
return ;
}
int org = g[u];
g[u] = min(org, f[rs]); dp2(ls, u);
g[u] = min(org, f[ls]); dp2(rs, u);
g[u] = min(org, min(f[ls], f[rs]));
return ;
}
int Ans[maxn], cnta;
void dfs(int u, int fa, bool type) {
int ls = -1, rs = -1;
for(int e = head[u]; e; e = nxt[e]) if(to[e] != fa) {
if(ls < 0) ls = to[e]; else rs = to[e];
}
if(ls < 0 && rs < 0){ Ans[++cnta] = u; return ; }
if(rs < 0) {
if(type) Ans[++cnta] = u, dfs(ls, u, 0);
else {
if(u == f[u]) Ans[++cnta] = u, dfs(ls, u, 0);
else dfs(ls, u, 0), Ans[++cnta] = u;
}
return ;
}
if(f[ls] > f[rs]) swap(ls, rs);
if(type) Ans[++cnta] = u, dfs(ls, u, 0), dfs(rs, u, 1);
else dfs(ls, u, 0), Ans[++cnta] = u, dfs(rs, u, 0);
return ;
}
int main() {
n = read();
int rt;
rep(i, 1, n) {
deg[i] = read();
rep(j, 1, deg[i]) AddEdge(i, read());
if(deg[i] < 3) rt = i;
}
if(n == 1) return puts("1"), 0;
dp1(rt, 0);
dp2(rt, 0);
int root = n + 1;
rep(i, 1, n) root = min(root, g[i]);
// printf("g: "); rep(i, 1, n) printf("%d%c", g[i], i < n ? ' ' : '\n');
dp1(root, 0);
dfs(root, 0, 1);
rep(i, 1, cnta) printf("%d%c", Ans[i], i < cnta ? ' ' : '\n');
return 0;
}
[LOJ#2324]「清华集训 2017」小Y和二叉树的更多相关文章
- Loj #2324. 「清华集训 2017」小 Y 和二叉树
Loj #2324. 「清华集训 2017」小 Y 和二叉树 小Y是一个心灵手巧的OIer,她有许多二叉树模型. 小Y的二叉树模型中,每个结点都具有一个编号,小Y把她最喜欢的一个二叉树模型挂在了墙上, ...
- [LOJ#2323]「清华集训 2017」小Y和地铁
[LOJ#2323]「清华集训 2017」小Y和地铁 试题描述 小Y是一个爱好旅行的OIer.一天,她来到了一个新的城市.由于不熟悉那里的交通系统,她选择了坐地铁. 她发现每条地铁线路可以看成平面上的 ...
- loj #2325. 「清华集训 2017」小Y和恐怖的奴隶主
#2325. 「清华集训 2017」小Y和恐怖的奴隶主 内存限制:256 MiB时间限制:2000 ms标准输入输出 题目类型:传统评测方式:文本比较 题目描述 "A fight? Co ...
- loj2324 「清华集训 2017」小 Y 和二叉树
https://loj.ac/problem/2324 太智障,一开始以为中序遍历的第一个点一定是一个叶子,想了个贪心.然而,手算了一下,第一个点都过不了啊. input 5 2 3 4 1 3 3 ...
- LOJ2324. 「清华集训 2017」小 Y 和二叉树【贪心】【DP】【思维】【好】
LINK 思路 首先贪新的思路是处理出以一个节点为根所有儿子的子树中中序遍历起始节点最小是多少 然后这个可以两次dfs来DP处理 然后就试图确定中序遍历的第一个节点 一定是siz<=2的编号最小 ...
- LOJ2324「清华集训 2017」小Y和二叉树
题目链接 瞎jb贪一发就过了.首先度数<=2且编号最小的点一定是中序遍历最靠前的点,我们从这个点开始dfs一遍算出子树中度数<=2且编号最小的点记为\(f(i)\),然后从这个点开始一步一 ...
- LibreOJ #2325. 「清华集训 2017」小Y和恐怖的奴隶主(矩阵快速幂优化DP)
哇这题剧毒,卡了好久常数才过T_T 设$f(i,s)$为到第$i$轮攻击,怪物状态为$s$时对boss的期望伤害,$sum$为状态$s$所表示的怪物个数,得到朴素的DP方程$f(i,s)=\sum \ ...
- 【loj2325】「清华集训 2017」小Y和恐怖的奴隶主 概率dp+倍增+矩阵乘法
题目描述 你有一个m点生命值的奴隶主,奴隶主受伤未死且当前随从数目不超过k则再召唤一个m点生命值的奴隶主. T次询问,每次询问如果如果对面下出一个n点攻击力的克苏恩,你的英雄期望会受到到多少伤害. 输 ...
- LOJ2325. 「清华集训 2017」小 Y 和恐怖的奴隶主【矩阵快速幂优化DP】【倍增优化】
LINK 思路 首先是考虑怎么设计dp的状态 发现奴隶主的顺序没有影响,只有生命和个数有影响,所以就可以把每个生命值的奴隶主有多少压缩成状态就可以了 然后发现无论是什么时候一个状态到另一个状态的转移都 ...
随机推荐
- 将数据库数据添加到ListView控件中
实现效果: 知识运用: ListView控件中的Items集合的Clear方法 //从listView控件的数据项集合中移除所有数据项 补充:可以使用Remove或RemoveAt方法从集合中移除单个 ...
- 复习C++_基础、函数、数组、字符串
程序的开发过程 程序 源程序:用源语言写的,有待翻译的程序: 目标程序:源程序通过翻译程序加工以后生成的机器语言程序: 可执行程序:连接目标程序以及库中的某些文件,生成的一个可执行文件,例如Windo ...
- Cesium专栏-测量工具测距、测面、测高(附源码下载)
Cesium Cesium 是一款面向三维地球和地图的,世界级的JavaScript开源产品.它提供了基于JavaScript语言的开发包,方便用户快速搭建一款零插件的虚拟地球Web应用,并在性能,精 ...
- Bootstrap历练实例:按钮(Button)插件单个切换
单个切换 如需激活单个按钮的切换(即改变按钮的正常状态为按压状态,反之亦然),只需向 button 元素添加 data-toggle="button" 作为其属性即可,如下面实例所 ...
- 海量数据GPS定位数据库表设计
在开发工业系统的数据采集功能相关的系统时,由于数据都是定时上传的,如每20秒上传一次的时间序列数据,这些数据在经过处理和计算后,变成了与时间轴有关的历史数据(与股票数据相似,如下图的车辆行驶过程中的油 ...
- ceph 性能
mysql在以下设备备份耗时,供大家参考: 备份文件大小 sata用时 ceph用时 nas挂载sata盘用时 7G 1分钟 15G 2分钟 21分钟 47G 8分钟 82分钟 274 ...
- Java - 若try中有return语句,finally会执行吗?在return之前还是之后呢?
会执行,在方法return动作之前,return语句执行之后,若finally中再有return语句,则此方法以finally的return作为最终返回,若finally中无return语句,则此方法 ...
- 判断IP连接数前五,并自动加入防火墙
#!/bin/bash #Author Template #Time -- : log_file=/tmp/tmp.log JudgeExt(){ if expr "$1" : & ...
- jquery/js/a标签实现当前页面跳转的两种方法
在逛购物网站首页时经常看到侧边导航栏,当我们点击导航栏中某一项时会跳转到当前页面的某一处 有两种方法实现,一种是利用js计算好各位置的高度,通过绑定事件使页面跳转到指定位置,另一种是利用a标签进行当前 ...
- 【linux】CPU,内存对网站的影响
如果读写非常多,建议内存大点 如果涉及到的计算非常多,那就升级CPU