标签:ace style while tom printf 直接 warning win src
题目传送门
题目大意
给定一棵以1为根的树,定义$dp_{k}(u)$表示在$u$的子树内存在的深度最大的满k叉树的深度,求$\sum_{u = 1}^{n}\sum_{k = 1}^{n}dp_{k}(u)$。
以某个点$x$为根存在一棵深度$m$的满$k$叉树是指,它满足下面任意一条:
先讲讲我的挂掉的做法。
因为是满$k$叉树,所以$dp_{k}(u)\leqslant \log_{k}n, k > 1$,由此推出当$k \geqslant \sqrt{n}$时,$dp_{k}(u) \leqslant 2$。
所以有了以下做法:
看起来$O(n\sqrt{n})$非常地优秀,题解也说。
然而实际上:
因为$dp_{k}(u) \leqslant \log_{k}n$,所以当$k > 1$的时候$dp_{k} \leqslant \log_{2}n$。然后反转值和下标,考虑什么时候取每个值。
我们同样注意到$dp_{k}$的某些不严格单调的性质。
设$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)$。
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 }
标签:ace style while tom printf 直接 warning win src
原文地址:https://www.cnblogs.com/yyf0309/p/9693586.html