「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_ ...
随机推荐
- [php]referer应用--http防盗链技术
1.防盗链的理解 所谓防盗链是防止其他的网站引用自己网站的资源连接,比如图片.视频等等,但是并不会阻碍从自己网站上享受资源的用户,这就要求能够将其他网站的连接请求阻止 2.防盗链的原理 其实从自己网站 ...
- 【Codeforces752D】Santa Claus and a Palindrome [STL]
Santa Claus and a Palindrome Time Limit: 20 Sec Memory Limit: 512 MB Description 有k个串,串长都是n,每个串有一个a ...
- 【Foreign】减法 [二分][贪心]
减法 Time Limit: 10 Sec Memory Limit: 256 MB Description 给你一个n个数的序列A,并且给出m次操作B. 操作的含义是:每次从A中选出不同的B_i个 ...
- 关于HttpWebRequest发生服务器协议冲突的解决办法
WinForm下的app.config文件中添加: <system.net> <settings> <httpWebRequest useUnsafeHe ...
- 【leetcode 简单】第三题 回文数
判断一个整数是否是回文数.回文数是指正序(从左向右)和倒序(从右向左)读都是一样的整数. 示例 1: 输入: 121 输出: true 示例 2: 输入: -121 输出: false 解释: 从左向 ...
- bzoj 2303 并查集
首先如果没有限制的话,我们可以直接求出答案,假设对于n*m的矩阵,我们最上方一行和左方的一列随意确定,那么首先这写确定的状态肯定是不会不合法的,因为我们可以调整剩下的01状态来使得这一行一列的状态合法 ...
- P2622 关灯问题II (状态压缩入门)
题目链接: https://www.luogu.org/problemnew/show/P2622 具体思路:暴力,尝试每个开关,然后看所有的情况中存不存在灯全部关闭的情况,在储存所有灯的情况的时候, ...
- 搭建linux+nginx+mysql+php环境
yum install -y gcc gcc-c++ make zlib zlib-devel pcre pcre-devel libjpeg libjpeg-devel libpng libpn ...
- hash算法搜索获得api函数地址的实现,"kernel32.dll", "CreateThread"
我们一般要获得一个函数的地址,通常采用的是明文,例如定义一个api函数字符串"MessageBoxA",然后在GetProcAddress函数中一个字节一个字节进行比较.这样弊端很 ...
- 【Git/GitHub学习笔记】一键更新多个git仓库至远程
因为同时在本地维护几个Github的仓库,每次更新后每个仓库要重复三步提交同步,有点麻烦. 发现可以写.sh文件来实现一键更新. 比如我要更新我的BlogBackup和CodeRepo两个仓库的代码如 ...