其实这道题和虚树没有太大的关系,我只是提一下而已。
因为点的权值很特殊,所以相当于要求剩下序列(从大到小)的字典序最大。
然后过程就比较显然了。从大到小枚举点,判断能否保留它(计算加入它后,新的树的点数有没有超过限制)。保留它是指和已经被保留的点连通。
这个相当于使得一些关键点连通,然后求出总点数。显然它可以用虚树来做。所以考虑虚树的构造算法,于是我们成功得到了一个时间复杂度为一个天文数字的算法。
将一个关键点添加进已有的树(暂时把保留的点形成的树这么称呼吧)中,无非情况有两种:
- 从这个关键点往上跳,直到跳到某个在已有的树中的点,经过点均被加入已有的树中。这种情况出现当且仅当关键点存在于已有的树的根(离原树钦定的根最近的一个点)在原树的子树中。
- 这个关键点到已有的树的根的路径上所有点被加入已有的树。(其他情况)
显然,这个过程不能纯暴力做。首先需要计算新添加的点的个数,如果能够保留它,那么暴力修改(因为修改的节点数等于$(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 }