「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_ ...
随机推荐
- 转:国内优秀npm镜像推荐及使用
原文:http://riny.net/2014/cnpm/ npm全称Node Package Manager,是node.js的模块依赖管理工具.由于npm的源在国外,所以国内用户使用起来各种不方便 ...
- hdu 1498 50 years, 50 colors(二分匹配_匈牙利算法)
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1498 50 years, 50 colors Time Limit: 2000/1000 MS (Ja ...
- 学习Python函数笔记之二(内置函数)
---恢复内容开始--- 1.内置函数:取绝对值函数abs() 2.内置函数:取最大值max(),取最小值min() 3.内置函数:len()是获取序列的长度 4.内置函数:divmod(x,y),返 ...
- 使用JSON语法创建JS对象(重要)
JS对象的键值可以加单引号或者不加或者加双引号 JSON语法提供了一种更简单的方式来创建对象,可以避免书写函数,也可避免用new关键字,可以直接创建一个JS对象,使用一个花括号,然后将每个属性写成&q ...
- perl6 HTTP::UserAgent (3) JSON
如果一个 URL 要求POST数据是 JSON格式的, 那我们要怎么发送数据呢? 第一种: HTTP::Request 上一篇说到, 发送 POST 数据, 可以: . $ua.post(url, % ...
- Spring 控制台运行及RestTemplate实现Eurka负载均衡
spring使用控制台运行方式 spring.main.web-application-type=none新老版本的配置有点差异 Maven的modules只是实现了一个顺序编译,一次多个项目一起生成 ...
- [转载]FFmpeg完美入门[4] - FFmpeg应用实例
1 用FFserver从文件生成流媒体 一.安装ffmpeg 在ubuntu下,运行sudo apt-get ffmpeg 安装ffmpeg,在其他linux操作系统下,见ffmpeg的编译过程(编译 ...
- 手机页面或是APP中减少使用setTimeout和setInterval,因为他们会导致页面卡顿
1.setTimeout致使页面的卡顿或是不流畅,打乱模块的生命周期 ,还有setTimeout其实是很难调试的. 当一个页面有众多js代码的时候,setTimeout就是导致页面的卡顿. var s ...
- sendEmail实现邮件报警
安装 wget http://caspian.dotconf.net/menu/Software/SendEmail/sendEmail-v1.56.tar.gz 或者点击下载 tar -xf sen ...
- java基础12 抽象类(及关键字:abstract)
抽象类:abstract 1.应用的场景 我们描述一类事物时,存在着某种行为,但这种行为目前不具体,那么我们就可以抽取这种行为的声明,但是不去实现这种行为,我们就需要使用抽象类. 2.抽象的好处 强制 ...