码迷,mamicode.com
首页 > 其他好文 > 详细

Codeforces 955F Heaps - 动态规划

时间:2018-09-23 22:27:25      阅读:284      评论:0      收藏:0      [点我收藏+]

标签:ace   style   while   tom   printf   直接   warning   win   src   

题目传送门

  传送点I

  传送点II

  传送点III

题目大意

  给定一棵以1为根的树,定义$dp_{k}(u)$表示在$u$的子树内存在的深度最大的满k叉树的深度,求$\sum_{u = 1}^{n}\sum_{k = 1}^{n}dp_{k}(u)$。

  以某个点$x$为根存在一棵深度$m$的满$k$叉树是指,它满足下面任意一条:

  1. $m = 1$。
  2. 当$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

 1 /**
 2  * Codeforces
 3  * Problem#955F
 4  * Accepted
 5  * Time: 327ms
 6  * Memory: 66900k
 7  */
 8 #include <bits/stdc++.h>
 9 #ifndef WIN32
10 #define Auto "%lld"
11 #else
12 #define Auto "%I64d"
13 #endif
14 using namespace std;
15 typedef bool boolean;
16 #define ll long long
17 #define pii pair<int, int>
18 #define fi first
19 #define sc second
20 
21 const int N = 3e5 + 5, bzmax = 20;
22 
23 int n;
24 int h[N];
25 int f[N][bzmax];
26 int buf[N], par[N];
27 vector<pii> upd[N];
28 vector<int> g[N];
29 ll res;
30 int dres;
31 
32 inline void init() {
33     scanf("%d", &n);
34     for (int i = 1, u, v; i < n; i++) {
35         scanf("%d%d", &u, &v);
36         g[u].push_back(v);
37         g[v].push_back(u);
38     }
39 }
40 
41 int dp(int p, int fa) {
42     int rt = 0;
43     par[p] = fa;
44 
45     for (int i = 0; i < (signed) g[p].size(); i++) {
46         int e = g[p][i];
47         if (e ^ fa)
48             rt = max(rt, dp(e, p));
49     }
50 
51     f[p][1] = n;
52     for (int t = 2; t < bzmax; t++) {
53         int tp = 0;
54         for (int i = 0; i < (signed) g[p].size(); i++) {
55             int e = g[p][i];
56             if ((e ^ fa) && f[e][t - 1])
57                 buf[++tp] = f[e][t - 1];
58         }
59         
60         sort(buf + 1, buf + tp + 1, greater<int>());
61 
62         for (int i = tp; i && !f[p][t]; i--)
63             if (buf[i] >= i)
64                 f[p][t] = i;
65         if (f[p][t] > 1)
66             upd[f[p][t]].push_back(pii(p, t));
67     }
68 
69     res += rt + 1;
70     return rt + 1;
71 }
72 
73 void update(int p, int v) {
74     while (p) {
75         if (v <= h[p])
76             break;
77         dres += v - h[p];
78         h[p] = v, p = par[p];
79     }
80 }
81 
82 inline void solve() {
83     dp(1, 0);
84     dres = n;
85     for (int i = 1; i <= n; i++)
86         h[i] = 1;
87     for (int k = n; k > 1; k--) {
88         for (int i = 0; i < (signed) upd[k].size(); i++)
89             update(upd[k][i].fi, upd[k][i].sc);
90         res += dres;
91     }
92     printf(Auto, res);
93 }
94 
95 int main() {
96     init();
97     solve();
98     return 0;
99 }

Codeforces 955F Heaps - 动态规划

标签:ace   style   while   tom   printf   直接   warning   win   src   

原文地址:https://www.cnblogs.com/yyf0309/p/9693586.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!