Codeforces 955F Heaps - 动态规划
题目传送门
题目大意
给定一棵以1为根的树,定义$dp_{k}(u)$表示在$u$的子树内存在的深度最大的满k叉树的深度,求$\sum_{u = 1}^{n}\sum_{k = 1}^{n}dp_{k}(u)$。
以某个点$x$为根存在一棵深度$m$的满$k$叉树是指,它满足下面任意一条:
- $m = 1$。
- 当$m \neq 1$时,$x$存在$k$个子节点,分别以它们为根存在一棵深度为$m - 1$的满$k$叉树。
先讲讲我的挂掉的做法。
因为是满$k$叉树,所以$dp_{k}(u)\leqslant \log_{k}n, k > 1$,由此推出当$k \geqslant \sqrt{n}$时,$dp_{k}(u) \leqslant 2$。
所以有了以下做法:
- 当$1\leqslant k < \sqrt{n}$时,我们进行$O(n)$的动态规划。设$f_{u}$表示以点$u$为根,存在的深度最大的满$k$叉树的深度。转移取子节点中第$k$大的$f$值在加1,不存在就是1。当然这里找$k$大要用 nth_element 。
- 当$k \geqslant \sqrt{n}$时,直接通过度数计算。
看起来$O(n\sqrt{n})$非常地优秀,题解也说
。
然而实际上:

因为$dp_{k}(u) \leqslant \log_{k}n$,所以当$k > 1$的时候$dp_{k} \leqslant \log_{2}n$。然后反转值和下标,考虑什么时候取每个值。
我们同样注意到$dp_{k}$的某些不严格单调的性质。
- 如果$u$是$v$的父节点,那么$dp_{k}(u) \geqslant dp_{k}(v)$
- 若$a\leqslant b$,则$dp_{a}(u) \geqslant dp_{b}(u)$。
设$h_{k, i}$表示以$i$为根存在的最深的满$k$叉树的深度。
如果某个$h$值被增大,那么我就沿着它的父节点向上跳去更新$dp$值,直到某个点已经不能更新。这样能够保证这一部分时间复杂度是$O(n\log n)$。
因为要将值增大,那就从大到小考虑每个$k$。
同时我们设$f_{i, j}$表示使得$h_{k}(i) \geqslant j$ 成立的最大的$j$。
转移很简单,我们找最大的$k$,使得子节点中第$k$大的$f_{s, j - 1}\geqslant k$。这个可以排个序然后直接扫。
然后用刚刚的方法更新$\Delta answer$,然后就做完了。
总时间复杂度$O(n\log_{2}^{2} n)$。
Code
/**
* Codeforces
* Problem#955F
* Accepted
* Time: 327ms
* Memory: 66900k
*/
#include <bits/stdc++.h>
#ifndef WIN32
#define Auto "%lld"
#else
#define Auto "%I64d"
#endif
using namespace std;
typedef bool boolean;
#define ll long long
#define pii pair<int, int>
#define fi first
#define sc second const int N = 3e5 + , bzmax = ; int n;
int h[N];
int f[N][bzmax];
int buf[N], par[N];
vector<pii> upd[N];
vector<int> g[N];
ll res;
int dres; inline void init() {
scanf("%d", &n);
for (int i = , u, v; i < n; i++) {
scanf("%d%d", &u, &v);
g[u].push_back(v);
g[v].push_back(u);
}
} int dp(int p, int fa) {
int rt = ;
par[p] = fa; for (int i = ; i < (signed) g[p].size(); i++) {
int e = g[p][i];
if (e ^ fa)
rt = max(rt, dp(e, p));
} f[p][] = n;
for (int t = ; t < bzmax; t++) {
int tp = ;
for (int i = ; i < (signed) g[p].size(); i++) {
int e = g[p][i];
if ((e ^ fa) && f[e][t - ])
buf[++tp] = f[e][t - ];
} sort(buf + , buf + tp + , greater<int>()); for (int i = tp; i && !f[p][t]; i--)
if (buf[i] >= i)
f[p][t] = i;
if (f[p][t] > )
upd[f[p][t]].push_back(pii(p, t));
} res += rt + ;
return rt + ;
} void update(int p, int v) {
while (p) {
if (v <= h[p])
break;
dres += v - h[p];
h[p] = v, p = par[p];
}
} inline void solve() {
dp(, );
dres = n;
for (int i = ; i <= n; i++)
h[i] = ;
for (int k = n; k > ; k--) {
for (int i = ; i < (signed) upd[k].size(); i++)
update(upd[k][i].fi, upd[k][i].sc);
res += dres;
}
printf(Auto, res);
} int main() {
init();
solve();
return ;
}
Codeforces 955F Heaps - 动态规划的更多相关文章
- Codeforces Flipping game 动态规划基础
题目链接:http://codeforces.com/problemset/problem/327/A 这道题目有O(N^3)的做法,这里转化为动态规划求解,复杂度是O(N) #include < ...
- Codeforces Gym 101623A - 动态规划
题目传送门 传送门 题目大意 给定一个长度为$n$的序列,要求划分成最少的段数,然后将这些段排序使得新序列单调不减. 考虑将相邻的相等的数缩成一个数. 假设没有分成了$n$段,考虑最少能够减少多少划分 ...
- Codeforces 1110D. Jongmah 动态规划
原文链接https://www.cnblogs.com/zhouzhendong/p/CF1110D.html 题意 给定 n 个数,每一个数都是在 [1,m] 里的整数. 从中取出形如 {x,x,x ...
- codeforces 955F Cowmpany Cowmpensation 树上DP+多项式插值
给一个树,每个点的权值为正整数,且不能超过自己的父节点,根节点的最高权值不超过D 问一共有多少种分配工资的方式? 题解: A immediate simple observation is that ...
- 【动态规划】Codeforces 711C Coloring Trees
题目链接: http://codeforces.com/problemset/problem/711/C 题目大意: 给N棵树,M种颜色,已经有颜色的不能涂色,没颜色为0,可以涂色,每棵树I涂成颜色J ...
- 【动态规划】【最短路】Codeforces 710E Generate a String
题目链接: http://codeforces.com/problemset/problem/710/E 题目大意: 问写N个字符的最小花费,写一个字符或者删除一个字符花费A,将当前的字符数量翻倍花费 ...
- 【动态规划】Codeforces 698A & 699C Vacations
题目链接: http://codeforces.com/problemset/problem/698/A http://codeforces.com/problemset/problem/699/C ...
- 【动态规划】Codeforces 706C Hard problem
题目链接: http://codeforces.com/contest/706/problem/C 题目大意: n(2 ≤ n ≤ 100 000)个字符串(长度不超过100000),翻转费用为Ci( ...
- Codeforces 932G Palindrome Partition - 回文树 - 动态规划
题目传送门 通往???的传送点 通往神秘地带的传送点 通往未知地带的传送点 题目大意 给定一个串$s$,要求将$s$划分为$t_{1}t_{2}\cdots t_{k}$,其中$2\mid k$,且$ ...
随机推荐
- js如何获取字符串第几次出现的位置
str:查询字符串: cha:查询子字符串: num:第几次出现:第一次则为 1: function findStrIndex(str, cha, num-1) { var x = str.index ...
- 数据交互axios的用法
参考: https://www.cnblogs.com/zhouyangla/p/6753673.html
- HashSet的底层
今天看博客,发现有个哥哥写到 HashSet的底层是数组结构,有点懵逼,不是HashMap吗怎么成数组了? 遂去查了下源码: 得以下结论: HashSet底层是 HashMap,其元素是内部map的k ...
- 剑指offer——python【第56题】删除链表中的重复节点
题目描述 在一个排序的链表中,存在重复的结点,请删除该链表中重复的结点,重复的结点不保留,返回链表头指针. 例如,链表1->2->3->3->4->4->5 处理后 ...
- javascript的数组之from()
Array.from()方法从一个类似数组或可迭代对象中创建一个新的数组实例. const arr = [1, 2, 3]; Array.from(arr); //[1, 2, 3] Array.fr ...
- 线程同步-使用SimaphoreSlim类
SimaphoreSlim类是作为Semaphore类的轻量级版本的.该类限制了同时访问同一个资源的线程数量. 代码Demo: using System;using System.Threading; ...
- (四)juc线程高级特性——线程池 / 线程调度 / ForkJoinPool
13. 线程池 第四种获取线程的方法:线程池,一个 ExecutorService,它使用可能的几个池线程之一执行每个提交的任务,通常使用 Executors 工厂方法配置. 线程池可以解决两个不同问 ...
- [摘录] 图灵机与lambda演算的关系
在阅读函数式编程相关资料时,看到如下一段话.感觉说的很好,可以帮助我这种学渣一点点的建立起整个知识体系. 以下片段,摘抄自豆瓣网友 赛义甫 的豆列 “逻辑与计算” 中的一段介绍. 莱布尼兹曾经有两个梦 ...
- C#4.0 HTTP协议无法使用TLS1.2的问题
在发送HTTP请求前加入下行代码 ServicePointManager.SecurityProtocol = (SecurityProtocolType) | (SecurityProtocolTy ...
- array_merge() 用法
array_merge() 函数把两个或多个数组合并为一个数组. 如果键名有重复,该键的键值为最后一个键名对应的值(后面的覆盖前面的).如果数组是数字索引的,则键名会以连续方式重新索引. 注释:如果仅 ...