「COCI2016/2017 Contest #2」Bruza
「COCI2016/2017 Contest #2」Bruza
解题思路 :
首先对于任意时刻 \(i\) ,硬币一定移动到了深度为 \(i\) 的节点,所以第 \(i\) 时刻 Danel 一定染掉一个深度为 \(i + 1\) 的节点。又因为如果硬币到了深度为 \(k\) 的节点游戏就结束了,所以深度 \(> k\) 的节点都可以忽视,把所有深度 \(= k\) 的节点看做这棵树的叶子,如果一个节点其子树里面没有深度 \(= k\) 的节点,那么这整棵子树也是可以被忽视的。
其次,如果染色的一个节点是另外一个节点的祖先,那么深度较深的那个节点被染是没有意义的。
那么每次染掉一个深度为 \(i\) 的节点,其至少会使得 \(k-i+1\) 个节点无法到达,那么染 \(k\) 次能染掉的节点数量的下界就是:
\]
于是可以得到一个关于 \(k\) 的较松的上界,当满足时 Danel 必胜:
\]
实际上这个上界是比较松的,可以继续证明并加以利用。观察发现如果染掉一个没有分叉的节点,等价于染掉其子树中第一个有分叉的节点。那么可以除了根节点以外,每次染掉深度最小的一个有分叉的节点,且要保证任意时刻 \(i\) ,染掉的深度 \(\leq i\) 的节点个数必须 $\leq i $ 。
显然,如果每种深度的节点有分叉的仅有一个,那么Danel必胜。
假设在某一时刻出现两个深度最小为 \(d\) 且有分叉的节点,那么其中一个有分叉的节点不能被染掉。相当于转化为进入这个节点所对应的子树的一个子问题,而在这之前,通过染色使得不能到达的最少节点数量之和是:
\]
解释一下这个式子,假设之前的被染色的每一个节点都只有两个分叉,且这两个分叉对应的子树都是以两条链的形式存在的,这样显然是最少的情况,此时被减少至不能再到达的部分就是该节点到根的路径以及这两个分叉。
令 \(S(n, k)\) 为初始状态的规模,此时进入的子问题的规模是 \(S(n', k')\) ,根据上述分析一下可以得到:
n' = n - 2kd+\frac{d(d+1)}{2} \\
n' \leq n - 2kd + d^2
\]
在这里假设一个更小的上界使得当满足时 Danel 必胜:
k^2 \geq n
\]
那么之前的式子
n' \leq k^2 - 2kd + d^2 \\
n' \leq (k -d)^2
\]
此时 \(S(n',k')\) 仍然满足假设的上界 \(k \geq \sqrt{n}\) ,这里归纳证明得到了一个更小的上界当满足时 Danel 必胜。
把问题带回最初的贪心思路,第 \(i\) 次选择深度为 \(i+1\) 的节点一定是最优的,那么深度 \([2,k]\) 每种只能染最多一个节点,且最终要使得所有叶子都有一个祖先在染色的点集里面。
把问题转化到反dfn序上,令 \(dp(i, s)\) 表示反dfn序前 \(i\) 为已经选了深度集合为 \(s\) 的点能覆盖的最多叶子数量,为了防止对叶子的贡献重复计数,同一子树内不能同时选,那么转移的时候讨论一下就好了
\]
其中 \(sz[i]\) 是反dfn序上第 \(i\) 位对应的节点的大小,\(val[i]\) 是这个节点的子树中的叶子节点数量,最后看一下是否存在一个 \(dp\) 状态能覆盖所有叶子即可,总复杂度 \(O(n2^\sqrt{n})\)。
据说还有 \(O(\sqrt{n}2^{\sqrt{n}})\) 的做法, 又据说一言难尽,改天再填坑吧。
code
/*program by mangoyang*/
#pragma GCC optimize("Ofast","inline","-ffast-math")
#pragma GCC target("avx,sse2,sse3,sse4,mmx")
#include<bits/stdc++.h>
#define inf (0x7f7f7f7f)
#define Max(a, b) ((a) > (b) ? (a) : (b))
#define Min(a, b) ((a) < (b) ? (a) : (b))
typedef long long ll;
using namespace std;
template <class T>
inline void read(T &x){
int ch = 0, f = 0; x = 0;
for(; !isdigit(ch); ch = getchar()) if(ch == '-') f = 1;
for(; isdigit(ch); ch = getchar()) x = x * 10 + ch - 48;
if(f) x = -x;
}
const int N = 405;
vector<int> g[N];
int f[N][(1<<17)+2], vi[N][(1<<17)+2];
int sz[N], d[N], dfn[N], mx[N], h[N], leaf, n, k, cnt, all, tot;
inline int MX(int x, int y){ return x > y ? x : y; }
inline int solve(int u, int s){
if(~f[u][s]) return f[u][s];
if(s == all || u > cnt) return f[u][s] = 0;
if(u == 1 || (1 << d[u]) & s || mx[u] < k) return f[u][s] = solve(u + 1, s);
return f[u][s] = MX(solve(u + sz[u], s | (1 << d[u])) + h[u], solve(u + 1, s));
}
inline void dfs(int u, int fa){
dfn[u] = ++cnt; sz[dfn[u]] = 1;
if(u > 1) d[dfn[u]] = d[dfn[fa]] + 1;
mx[dfn[u]] = d[dfn[u]];
if(d[dfn[u]] == k) return (void) (leaf++, h[dfn[u]] = 1);
for(int i = 0; i < g[u].size(); i++){
int v = g[u][i];
if(v == fa) continue;
dfs(v, u), sz[dfn[u]] += sz[dfn[v]], h[dfn[u]] += h[dfn[v]];
mx[dfn[u]] = MX(mx[dfn[u]], mx[dfn[v]]);
}
}
int main(){
memset(f, -1, sizeof(f));
read(n), read(k), all = (1 << k) - 1;
if(k * k >= n) return puts("DA"), 0;
for(int i = 1, x, y; i < n; i++){
read(x), read(y);
g[x].push_back(y), g[y].push_back(x);
}
dfs(1, 0);
for(int i = 1; i <= n; i++) d[i]--;
puts(solve(1, 0) >= leaf ? "DA" : "NE");
return 0;
}
「COCI2016/2017 Contest #2」Bruza的更多相关文章
- loj #6250. 「CodePlus 2017 11 月赛」找爸爸
#6250. 「CodePlus 2017 11 月赛」找爸爸 题目描述 小 A 最近一直在找自己的爸爸,用什么办法呢,就是 DNA 比对. 小 A 有一套自己的 DNA 序列比较方法,其最终目标是最 ...
- [LOJ#6259]「CodePlus 2017 12 月赛」白金元首与独舞
[LOJ#6259]「CodePlus 2017 12 月赛」白金元首与独舞 试题描述 到河北省 见斯大林 / 在月光下 你的背影 / 让我们一起跳舞吧 うそだよ~ 河北省怎么可能有 Stalin. ...
- [LOJ 6249]「CodePlus 2017 11 月赛」汀博尔
Description 有 n 棵树,初始时每棵树的高度为 H_i,第 i 棵树每月都会长高 A_i.现在有个木料长度总量为 S 的订单,客户要求每块木料的长度不能小于 L,而且木料必须是整棵树(即不 ...
- [LOJ 6248]「CodePlus 2017 11 月赛」晨跑
Description “无体育,不清华”.“每天锻炼一小时,健康工作五十年,幸福生活一辈子” 在清华,体育运动绝对是同学们生活中不可或缺的一部分.为了响应学校的号召,模范好学生王队长决定坚持晨跑.不 ...
- 「JOISC 2017 Day 3」幽深府邸
题解: 和hnoi2018day2t1基本一样 我想了半小时想出了一个很麻烦的做法 写了之后发现假掉了 刚开始想的是 先预处理出每个门要打开至少要在左边的哪个点$L[]$,右边的哪个点$R[]$ 对每 ...
- 「CodePlus 2017 11 月赛」Yazid 的新生舞会(树状数组/线段树)
学习了新姿势..(一直看不懂大爷的代码卡了好久T T 首先数字范围那么小可以考虑枚举众数来计算答案,设当前枚举到$x$,$s_i$为前$i$个数中$x$的出现次数,则满足$2*s_r-r > 2 ...
- 「CodePlus 2017 11 月赛」大吉大利,晚上吃鸡!(dij+bitset)
从S出发跑dij,从T出发跑dij,顺便最短路计数. 令$F(x)$为$S$到$T$最短路经过$x$的方案数,显然这个是可以用$S$到$x$的方案数乘$T$到$x$的方案数来得到. 然后第一个条件就变 ...
- 「CodePlus 2017 12 月赛」火锅盛宴(模拟+树状数组)
1A,拿来练手的好题 用一个优先队列按煮熟时间从小到大排序,被煮熟了就弹出来. 用n个vector维护每种食物的煮熟时间,显然是有序的. 用树状数组维护每种煮熟食物的数量. 每次操作前把优先队列里煮熟 ...
- 「CodePlus 2017 12 月赛」可做题2(矩阵快速幂+exgcd+二分)
昨天这题死活调不出来结果是一个地方没取模,凉凉. 首先有个一眼就能看出来的规律... 斐波那契数列满足$a_1, a_2, a_1+a_2, a_1+2a_2, 2a_1+3a_2, 3a_1+5a_ ...
随机推荐
- [csp-201809-3]元素选择器-编译原理
声明:同样是参考照抄hyh学长的代码!(有问题我马上删这篇emm 题目链接:http://118.190.20.162/view.page?gpid=T77 题面: 这棵树的样子(同样是来自学长的图) ...
- 【洛谷 P4360】 [CEOI2004]锯木厂选址(斜率优化)
题目链接 一开始我的\(dp\)方程列错了,其实也不能说列错了,毕竟我交上去还是把暴力的分都拿到了,只是和题解的不一样,然后搞半天没搞出来去看题解,又看不懂,对不上,原来状态设置不一样自闭了. \(f ...
- python初步学习-面向对象之类(一)
python 面向对象 python 从设计之初就已经是一门面向对象的语言,正因为如此,在python中创建一个类和对象是很容易的. 对象对象奇数简介 类(Class): 用于描述具有相同的属性和方法 ...
- 73.Vivado使用误区与进阶——在Vivado中实现ECO功能
关于Tcl在Vivado中的应用文章从Tcl的基本语法和在Vivado中的应用展开,继上篇<用Tcl定制Vivado设计实现流程>介绍了如何扩展甚至是定制FPGA设计实现流程后,引出了一个 ...
- for 、forEach 、 forof、 forin遍历对比
一.遍历内容的异同 1.for 和 for...in 是针对数组下标的遍历 2.forEach 及 for...of 遍历的是数组中的元素 二.对非数字下标的处理 由于array在js中也是对象中的一 ...
- git版本控制系统常见操作总结
简介 Git是强大的版本控制系统,主要功能是针对代码.配置文件等文本进行版本控制.备份等,同时个人认为还是分发代码的一个不错的方式. 常见用法 #创建远程git仓库 [root@test88 ~]# ...
- Maven核心概念
一.坐标 在平面几何中,坐标(x,y)可以标识平面中唯一的一个点.在maven中,坐标是为了定位一个唯一确定的jar包.Maven世界拥有大量构建,我们需要找一个用来唯一标识一个构建的统一规范:拥有了 ...
- final修饰的变量是引用不能改变还是引用的对象不能改变
我们都知道final修饰变量时 会变为常量,但是使 用final关键字修饰一个变量时,是引用不能变,还是引用的对象不能变? 下面让我们来看这段代码: /** * 验证final修饰的变量是引用不能变, ...
- Windows: 如何配置IPv6隧道
清空隧道配置: netsh interface ipv6 set teredo disable netsh interface ipv6 6to4 set state disable netsh in ...
- Linux软件管理器(如何使用软件管理器来管理软件)2---安装及管理Linux应用程序
安装及管理Linux应用程序 Linux应用程序的组成1.普通的可执行程序文件,一般保存在/usr/bin目录中,普通用户即可执行.2.服务器程序.管理程序文件,一般保存在/usr/sbin目录中,需 ...