Codeforces 980E The Number Games - 贪心 - 树状数组
其实这道题和虚树没有太大的关系,我只是提一下而已。
因为点的权值很特殊,所以相当于要求剩下序列(从大到小)的字典序最大。
然后过程就比较显然了。从大到小枚举点,判断能否保留它(计算加入它后,新的树的点数有没有超过限制)。保留它是指和已经被保留的点连通。
这个相当于使得一些关键点连通,然后求出总点数。显然它可以用虚树来做。所以考虑虚树的构造算法,于是我们成功得到了一个时间复杂度为一个天文数字的算法。
将一个关键点添加进已有的树(暂时把保留的点形成的树这么称呼吧)中,无非情况有两种:
- 从这个关键点往上跳,直到跳到某个在已有的树中的点,经过点均被加入已有的树中。这种情况出现当且仅当关键点存在于已有的树的根(离原树钦定的根最近的一个点)在原树的子树中。
- 这个关键点到已有的树的根的路径上所有点被加入已有的树。(其他情况)
显然,这个过程不能纯暴力做。首先需要计算新添加的点的个数,如果能够保留它,那么暴力修改(因为修改的节点数等于$(n - k)$)。
对于情况一,可以通过记录深度数组和倍增数组解决。
对于情况二,求完LCA用树上距离公式。
总时间复杂度$O(n\log n)$
Code
1 /** 2 * Codeforces 3 * Problem#980E 4 * Accepted 5 * Time: 1918ms 6 * Memory: 172700k 7 */ 8 #include <bits/stdc++.h> 9 using namespace std; 10 typedef bool boolean; 11 12 const int N = 1e6 + 5, bzmax = 21; 13 14 int n, m; 15 int cnt; 16 vector<int> *g; 17 int *in, *out; 18 int *dep; 19 boolean *vis; 20 int bz[N][bzmax]; 21 22 inline void init() { 23 scanf("%d%d", &n, &m); 24 m = n - m; 25 g = new vector<int>[(n + 1)]; 26 in = new int[(n + 1)]; 27 out = new int[(n + 1)]; 28 dep = new int[(n + 1)]; 29 vis = new boolean[(n + 1)]; 30 memset(vis, false, sizeof(boolean) * (n + 1)); 31 for (int i = 1, u, v; i < n; i++) { 32 scanf("%d%d", &u, &v); 33 g[u].push_back(v); 34 g[v].push_back(u); 35 } 36 } 37 38 void dfs(int p, int fa) { 39 in[p] = ++cnt, dep[p] = dep[fa] + 1; 40 bz[p][0] = fa; 41 for (int i = 1; i < bzmax; i++) 42 bz[p][i] = bz[bz[p][i - 1]][i - 1]; 43 for (int i = 0; i < (signed)g[p].size(); i++) { 44 int e = g[p][i]; 45 if (e == fa) continue; 46 dfs(e, p); 47 } 48 out[p] = cnt; 49 } 50 51 int lca(int u, int v) { 52 if (dep[u] < dep[v]) swap(u, v); 53 if (in[u] <= in[v] && out[u] >= out[v]) 54 return u; 55 for (int i = bzmax - 1, a; ~i; i--) { 56 a = bz[u][i]; 57 if (!(in[a] <= in[v] && out[a] >= out[v])) 58 u = a; 59 } 60 return bz[u][0]; 61 } 62 63 inline void solve() { 64 dep[0] = 0, vis[n] = true, m -= 1, in[0] = 0, out[0] = 1428571; 65 dfs(1, 0); 66 int vr = n; 67 for (int i = n - 1; i; i--) { 68 if (vis[i]) continue; 69 if (in[vr] <= in[i] && out[vr] >= out[i]) { 70 int u = i, v; 71 for (int j = bzmax - 1; ~j; j--) { 72 v = bz[u][j]; 73 if (dep[v] >= dep[vr] && !vis[v]) 74 u = v; 75 } 76 if (dep[i] - dep[u] + 1 > m) continue; 77 for (int j = i; j && !vis[j]; j = bz[j][0]) 78 vis[j] = true, m--; 79 } else { 80 int g = lca(vr, i); 81 // cerr << dep[i] + dep[vr] - (dep[g] << 1) << endl; 82 if (dep[i] + dep[vr] - (dep[g] << 1) > m) continue; 83 for (int j = i; j != bz[g][0] && !vis[j]; j = bz[j][0]) 84 vis[j] = true, m--; 85 for (int j = bz[vr][0]; !vis[j]; j = bz[j][0]) 86 vis[j] = true, m--; 87 vr = g; 88 } 89 } 90 for (int i = 1; i <= n; i++) 91 if (!vis[i]) 92 printf("%d ", i); 93 } 94 95 int main() { 96 init(); 97 solve(); 98 return 0; 99 }
我们完美解决了这个问题?不。做人要有追求,1.9s真不爽。
注意到$n$号点必定存在于已有的树中。那么直接钦定$n$号点作为原树的根。
这样直接消灭掉了情况二。对于情况一,也可以稍微改一改。因为原树的根与已有的树的根重合,可以直接树差分加上树状数组计算出距离。
Code
1 /** 2 * Codeforces 3 * Problem#980E 4 * Accepted 5 * Time: 888ms 6 * Memory: 98300k 7 */ 8 #include <bits/stdc++.h> 9 using namespace std; 10 typedef bool boolean; 11 12 typedef class IndexedTree { 13 public: 14 int *ar; 15 int s; 16 17 IndexedTree():ar(NULL) { } 18 IndexedTree(int s):s(s) { 19 ar = new int[(s + 1)]; 20 memset(ar, 0, sizeof(int) * (s + 1)); 21 } 22 23 void add(int idx, int val) { 24 for ( ; idx <= s; idx += (idx & (-idx))) 25 ar[idx] += val; 26 } 27 28 int getSum(int idx) { 29 int rt = 0; 30 for ( ; idx; idx -= (idx & (-idx))) 31 rt += ar[idx]; 32 return rt; 33 } 34 35 void add(int l, int r, int val) { 36 add(l, val), add(r + 1, -val); 37 } 38 }IndexedTree; 39 40 int n, m; 41 int cnt; 42 vector<int> *g; 43 int *in, *out; 44 int *dep, *fas; 45 boolean *vis; 46 IndexedTree it; 47 48 inline void init() { 49 scanf("%d%d", &n, &m); 50 m = n - m; 51 g = new vector<int>[(n + 1)]; 52 in = new int[(n + 1)]; 53 out = new int[(n + 1)]; 54 dep = new int[(n + 1)]; 55 fas = new int[(n + 1)]; 56 vis = new boolean[(n + 1)]; 57 it = IndexedTree(n); 58 memset(vis, false, sizeof(boolean) * (n + 1)); 59 for (int i = 1, u, v; i < n; i++) { 60 scanf("%d%d", &u, &v); 61 g[u].push_back(v); 62 g[v].push_back(u); 63 } 64 } 65 66 void dfs(int p, int fa) { 67 in[p] = ++cnt, dep[p] = dep[fa] + 1, fas[p] = fa; 68 for (int i = 0; i < (signed)g[p].size(); i++) { 69 int e = g[p][i]; 70 if (e == fa) continue; 71 dfs(e, p); 72 } 73 out[p] = cnt; 74 } 75 76 inline void solve() { 77 dep[0] = 0; 78 dfs(n, 0); 79 vis[n] = true, it.add(in[n], out[n], 1), m--; 80 for (int i = n - 1; i; i--) { 81 if (vis[i]) continue; 82 int added = dep[i] - it.getSum(in[i]); 83 if (added > m) continue; 84 for (int j = i; !vis[j]; j = fas[j]) 85 it.add(in[j], out[j], 1), vis[j] = true, m--; 86 } 87 for (int i = 1; i <= n; i++) 88 if (!vis[i]) 89 printf("%d ", i); 90 } 91 92 int main() { 93 init(); 94 solve(); 95 return 0; 96 }