[LOJ#2330]「清华集训 2017」榕树之心
[LOJ#2330]「清华集训 2017」榕树之心
试题描述
深秋。冷风吹散了最后一丝夏日的暑气,也吹落了榕树脚下灌木丛的叶子。相识数年的Evan和Lyra再次回到了小时候见面的茂盛榕树之下。小溪依旧,石桥依旧,榕树虽是历经荣枯更迭,依旧亭亭如盖,只是Evan和Lyra再也不是七八年前不经世事的少年了。
……
“已经快是严冬了,榕树的叶子还没落呢……”
“榕树是常绿树,是看不到明显的落叶季节的……”
“唉……想不到已经七年了呢。榕树还是当年的榕树,你却不是当年的你了……”
“其实又有什么是一成不变的呢,榕树常绿,翠绿树冠的宏观永恒,是由无数细小树叶的荣枯更迭组成的。在时间的流逝中一切都在不断变化着呢……”
“但你看这榕树,日日如此,季季如此,年年如此,仿佛亘古不变般,盘根错节,郁郁葱葱。我在想,或许成为一棵树更好吧,任时间从枝叶间流过,我只守这一片绿荫就好。”
“榕树固然长久,但在这无限的时光里,终归是要湮灭于尘土的。与其像榕树一般,植根于一方泥土中感受年复一年的四季更替。倒不如在有限的时间里看过尽可能多的世界吧。再说了,榕树虽生长缓慢,却依旧会在每年春天抽出一根新的枝条去向外探索的呢……”
“真的吗,榕树在她漫长的一生里,就是这样往外一步步探索的吗?”
“毕竟就算树冠看起来一成不变,榕树也会随着时间周期变化,春天到了自然就是生长的时候了,她也应当做出对应的表现吧……”
“相比于对季节更替做出本能的生长,我倒宁愿相信,榕树有一颗活跃的的,探索的心。”
“其实榕树是有心的,榕树刚刚种下的时候,心就在根的地方发芽了。以后每年春天榕树长出新枝条的时候,心就会向着新枝条的方向移动一点,这样就能更靠近外面的世界了。你看这头顶上的枝条,纵横交错,其实心已经在这枝杈间,移动了数十载了呢……”
“哇,也就是说,这密密麻麻的树杈中的某个地方,藏着这棵榕树的心吗?”
“没错,可是要知道它在哪,就得另花一番功夫了……”
“呀,这时候想想,一株树还是不如一个人好……比如你,要是这样贴上去的话,就能听到跳动的声音呢……”
……
一棵榕树可以抽象成一棵 \(n\) 个结点的有根树,其中结点编号为 \(1 \sim n\),而 \(1\) 号点就是根节点。初始时,树只有一号点,而心也在一号点。之后每一步,树都会长出一个新结点,即某个和当前已经存在的某个结点相邻的结点被加入了树中,之后,心会沿着心到新加结点的简单路径移动一步。这棵 \(n\) 个结点的树有很多种生长的顺序,不同的顺序可能会导致最终心的位置不同。现在,Evan和Lyra想知道,哪些结点可能是心在生长过程结束时停留的位置呢?
例如一棵大小为 \(4\) 的树,连边为 \(\{<1,2>,<1,3>,<1,4>\}\),我们有三种不同的生长顺序可以让心分别停留在 \(2,3,4\) 号节点上:
最终停留在 \(2\) 号点:
- 从 \(1\) 生长出 \(3\),心从 \(1\) 移动到 \(3\),
- 从 \(1\) 生长出 \(4\),心从 \(3\) 移动回 \(1\),
- 从 \(1\) 生长出 \(2\),心从 \(1\) 移动到 \(2\).
最终停留在 \(3\) 号点:
- 从 \(1\) 生长出 \(2\),心从 \(1\) 移动到 \(2\),
- 从 \(1\) 生长出 \(4\),心从 \(2\) 移动回 \(1\),
- 从 \(1\) 生长出 \(3\),心从 \(1\) 移动到 \(3\).
最终停留在 \(4\) 号点:
- 从 \(1\) 生长出 \(2\),心从 \(1\) 移动到 \(2\),
- 从 \(1\) 生长出 \(3\),心从 \(2\) 移动回 \(1\),
- 从 \(1\) 生长出 \(4\),心从 \(1\) 移动到 \(4\).
而我们可以证明,不存在任何一种可能的生长顺序使得心停留在 \(1\) 号点。
输入
从标准输入读入数据。
输入第一行一个两个正整数 \(W, T\),分别表示子任务编号(在样例中 \(W=0\))和数据组数,接下来是 \(T\) 组数据的描述,对于每组数据:
第一行一个正整数 \(n\) 表示树上结点的个数。
接下来 \(n-1\) 行,每行两个正整数 \(ia_i,b_i\),表示编号 \(a_i,b_i\) 的结点间有一条树边,保证 \(a_i \neq b_i\) 并且输入的 \(n-1\) 条边恰好构成了一棵树。
输出
输出到标准输出。
若输入的 \(W\) 不等于 \(3\),对于每组数据输出一行一个长度为 \(n\) 的 \(01\) 字符串,表示编号为 \(1 \sim n\) 的结点是否有可能是心最后所在的位置,若 \(01\) 字符串对应位是 \(1\) 则表示可能,为 \(0\) 则表示不可能。
若输入的 \(W\) 等于 \(3\),则对每组数据输出一个字符表示 \(1\) 号点的答案。
输入示例
0 3
4
1 2
1 3
1 4
6
1 2
1 3
1 4
4 5
5 6
10
1 2
1 3
3 4
3 5
3 6
4 7
7 8
8 9
9 10
输出示例
0111
000101
0000001010
数据规模及约定
Subtask 1[10pts]
\(T \leq 50; n \leq 15\)。
Subtask 2[10pts]
\(T \leq 20; n \leq 10^5\)。 除了 \(1\) 号点之外,每个点度数(包括父亲)不超过 \(2\)。
Subtask 3[10pts]
\(T \leq 200; n \leq 100\)。 只输出一个字符表示 \(1\) 号点答案,即保证 \(1\) 号点答案正确即可。
Subtask 4[35pts]
\(T \leq 20; n \leq 10^3\)。
Subtask 5[35pts]
\(T \leq 20; n \leq 10^5\)。
题解
考虑“生长”效果可以互相消去。先考虑只用判断能否到达根节点的子任务,我们发现当且仅当所有生长效果可以被消干净时才有可能最后停到根节点。
然后对于所有子树,最难消的肯定是最大的那颗子树,并且可以直观感觉到,最大的子树越小,越容易消干净,那么怎么量化这个感觉呢?
我们不妨设 \(elim(i)\) 表示对于子树 \(i\),它最多能消掉的对数,令 \(siz_i\) 表示子树 \(i\) 中的节点数,\(s_m\) 为 \(i\) 最大的儿子。
那么当 \(siz_{s_m} \le siz_i - 1 - siz_{s_m}\) 时,可以用其他点把子树 \(s_m\) 全部消掉,如果其他点多出来了,它们之间可以互相消掉,唯一可能剩下的情况就是 \(siz_i - 1\) 为奇数,那么肯定会剩下一个节点。
当 \(siz_{s_m} > siz_i - 1 - siz_{s_m}\) 时,就考虑先让儿子 \(s_m\) 自己消,消到一定时候我们用兄弟助他一臂之力,看能不能消光;即 \(2 \cdot elim(s_m) + siz_i - 1 - siz_{s_m} \ge siz_{s_m}\) 时,能够全消光(奇数的话剩一个),否则能消的对数就是 \(elim(s_m) + siz_i - 1 - siz_{s_m}\)。
那么再考虑满分做法,对于节点 \(x\),如果最终能够到达,相当于把 \(1 \sim x\) 的路径缩成新的根,然后其他的子树互相消就好了。这样我们就可以 dfs 的同时维护需要的信息(\(elim(i)\),\(\mathrm{max}\{siz_i\}\) 等,这些信息可以很容易地合并,所以能够维护),然后遍历到的点就可以判断是否能够到达了。
#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 100010
#define maxm 200010
int n, m, head[maxn], nxt[maxm], to[maxm];
void AddEdge(int a, int b) {
to[++m] = b; nxt[m] = head[a]; head[a] = m;
swap(a, b);
to[++m] = b; nxt[m] = head[a]; head[a] = m;
return ;
}
int siz[maxn], elim[maxn], ms[maxn], ms2[maxn];
void getElim(int u, int fa) {
siz[u] = 1;
ms[u] = ms2[u] = -1;
for(int e = head[u]; e; e = nxt[e]) if(to[e] != fa) {
getElim(to[e], u);
siz[u] += siz[to[e]];
if(ms[u] < 0) ms[u] = to[e];
else if(siz[to[e]] > siz[ms[u]]) ms2[u] = ms[u], ms[u] = to[e];
else if(ms2[u] < 0 || siz[to[e]] > siz[ms2[u]]) ms2[u] = to[e];
}
int others = 0;
for(int e = head[u]; e; e = nxt[e]) if(to[e] != fa && to[e] != ms[u])
others += siz[to[e]];
if(others >= siz[ms[u]] - (elim[ms[u]] << 1)) elim[u] = siz[u] - 1 >> 1;
else elim[u] = elim[ms[u]] + others;
return ;
}
bool can[maxn];
void getCan(int u, int fa, int nsiz, int nelim, int nothers) {
if(ms[u] < 0) {
// printf("leave %d: %d %d %d\n", u, nsiz, nelim, nothers);
if(nothers >= nsiz - (nelim << 1) && !(nsiz + nothers & 1)) can[u] = 1;
else can[u] = 0;
return ;
}
int siza, ela, others = nothers + siz[u] - siz[ms[u]] - 1;
if(siz[ms[u]] > nsiz) siza = siz[ms[u]], ela = elim[ms[u]], others += nsiz;
else siza = nsiz, ela = nelim, others += siz[ms[u]];
// printf("node %d: %d %d %d\n", u, siza, ela, others);
if(others >= siza - (ela << 1) && !(siza + others & 1)) can[u] = 1;
else can[u] = 0;
for(int e = head[u]; e; e = nxt[e]) if(to[e] != fa) {
int nxsiz, nxelim, nxothers = nothers;
if(to[e] == ms[u]) {
nxothers += siz[u] - siz[to[e]] - 1 - (ms2[u] < 0 ? 0 : siz[ms2[u]]);
if(ms2[u] < 0 || nsiz > siz[ms2[u]]) nxsiz = nsiz, nxelim = nelim, nxothers += siz[ms2[u]];
else nxsiz = siz[ms2[u]], nxelim = elim[ms2[u]], nxothers += nsiz;
getCan(to[e], u, nxsiz, nxelim, nxothers);
}
else {
nxothers += siz[u] - siz[to[e]] - 1 - siz[ms[u]];
if(nsiz > siz[ms[u]]) nxsiz = nsiz, nxelim = nelim, nxothers += siz[ms[u]];
else nxsiz = siz[ms[u]], nxelim = elim[ms[u]], nxothers += nsiz;
getCan(to[e], u, nxsiz, nxelim, nxothers);
}
}
return ;
}
int main() {
int W = read(), T = read();
while(T--) {
n = read();
m = 0; memset(head, 0, sizeof(head));
rep(i, 1, n - 1) {
int a = read(), b = read();
AddEdge(a, b);
}
memset(elim, 0, sizeof(elim));
getElim(1, 0);
// rep(i, 1, n) printf("%d%c", elim[i], i < n ? ' ' : '\n');
getCan(1, 0, 0, 0, 0);
if(W != 3){ rep(i, 1, n) putchar((int)can[i] + '0'); putchar('\n'); }
else{ putchar((int)can[1] + '0'); putchar('\n'); }
}
return 0;
}
[LOJ#2330]「清华集训 2017」榕树之心的更多相关文章
- 2018.09.01 loj#2330. 「清华集训 2017」榕树之心(树形dp)
传送门 树形dp好题啊. 我们用w[i]" role="presentation" style="position: relative;">w[ ...
- Loj #2331. 「清华集训 2017」某位歌姬的故事
Loj #2331. 「清华集训 2017」某位歌姬的故事 IA 是一名会唱歌的女孩子. IOI2018 就要来了,IA 决定给参赛选手们写一首歌,以表达美好的祝愿.这首歌一共有 \(n\) 个音符, ...
- Loj #2324. 「清华集训 2017」小 Y 和二叉树
Loj #2324. 「清华集训 2017」小 Y 和二叉树 小Y是一个心灵手巧的OIer,她有许多二叉树模型. 小Y的二叉树模型中,每个结点都具有一个编号,小Y把她最喜欢的一个二叉树模型挂在了墙上, ...
- Loj #2321. 「清华集训 2017」无限之环
Loj #2321. 「清华集训 2017」无限之环 曾经有一款流行的游戏,叫做 *Infinity Loop***,先来简单的介绍一下这个游戏: 游戏在一个 \(n \times m\) 的网格状棋 ...
- Loj 2320.「清华集训 2017」生成树计数
Loj 2320.「清华集训 2017」生成树计数 题目描述 在一个 \(s\) 个点的图中,存在 \(s-n\) 条边,使图中形成了 \(n\) 个连通块,第 \(i\) 个连通块中有 \(a_i\ ...
- [LOJ#2329]「清华集训 2017」我的生命已如风中残烛
[LOJ#2329]「清华集训 2017」我的生命已如风中残烛 试题描述 九条可怜是一个贪玩的女孩子. 这天她在一堵墙钉了 \(n\) 个钉子,第 \(i\) 个钉子的坐标是 \((x_i,y_i)\ ...
- [LOJ#2328]「清华集训 2017」避难所
[LOJ#2328]「清华集训 2017」避难所 试题描述 "B君啊,你当年的伙伴都不在北京了,为什么你还在北京呢?" "大概是因为出了一些事故吧,否则这道题就不叫避难所 ...
- [LOJ#2327]「清华集训 2017」福若格斯
[LOJ#2327]「清华集训 2017」福若格斯 试题描述 小d是4xx9小游戏高手. 有一天,小d发现了一个很经典的小游戏:跳青蛙. 游戏在一个 \(5\) 个格子的棋盘上进行.在游戏的一开始,最 ...
- [LOJ#2326]「清华集训 2017」简单数据结构
[LOJ#2326]「清华集训 2017」简单数据结构 试题描述 参加完IOI2018之后就是姚班面试.而你,由于讨厌物理.并且想成为乔布斯一样的创业家,被成功踢回贵系. 转眼,时间的指针被指向201 ...
随机推荐
- CentOS创建不需要输入密码的sudo用户
linux 怎么让普通用户使用sudo权限不需要输入密码. 在网上看到很多资料,都只说到了在/etc/sudoers添加下列行即可: username ALL=(ALL) ...
- Spring学习记录(一)
1.Spring简介 Spring是一个轻量级的java开发框架.框架的主要优势之一就是分层架构,Spring使用基本的JavaBean,不仅限于服务器的开发.从简单性,可测试性和松耦合的角度而言,任 ...
- PAT 乙级 1015
题目 题目地址:PAT 乙级 1015 题解 常规题,难点在于理清楚排序规则,通过比较简洁的方式进行编码: 在这里我选择使用vector进行存储,并使用sort方法排序,因为本题不是简单按照大小排序, ...
- Java - BigDecimal四舍五入注意事项
如上图,精度后只有一位时,是五舍六入. 如上图,精度后只有第一位不为0时,也是五舍六入. 如上图,精度后至少有两位不为0时,才是四舍五入.
- python 实现无序列表
# -*- coding:utf-8 -*- class Node: def __init__(self, initdata): self.data = initdata self.next = No ...
- DevOps - 配置管理 - Puppet
uppet总结 一.基础知识 1. Puppet是开源的基于Ruby的系统配置管理工具,依赖于C/S的部署架构.Puppet这样的自动化配置管理工具可以帮助系统管理员更加方便的完成多台服务器的升级软件 ...
- CentOS Linux 安装IPSec+L2TP
第二层隧道协议L2TP(Layer 2 Tunneling Protocol)是一种工业标准的Internet隧道协议,它使用UDP的1701端口进行通信.L2TP本身并没有任何加密,但是我们可以使用 ...
- Nuxt.js 基础入门教程
原文链接 Vue 开发一个单页面应用,相信很多前端工程师都已经学会了,但是单页面应用有一个致命的缺点,就是 SEO 极不友好.除非,vue 能在服务端渲染(ssr)并直接返回已经渲染好的页面,而并非只 ...
- [转载]本地配置的 *.dev,*.app域名 在谷歌浏览器中总是自动转跳到https上,导致不能访问?
本地开发环境 .dev 不正常,找到文章mark一下 转自:https://segmentfault.com/q/1010000012339191
- Python9-MySQL-pymysql模块-day44
import pymysql user = input('username: ') pwd = input('password: ') conn = pymysql.connect(host=',da ...