CodeForces 980 E The Number Games
题意:一共有n个点, n-1条路,第i个点有2^i个人, 然后需要删除k个点,现在需要删除k个点之后,剩下的图为连通图, 并且剩下的人数最多。
题解:我们可以发现如果删除最大的那个点 比 删除其他的所有点损失的人数还要多, 所以我们要贪心的留下标号大的点。我们将删除点的问题转变成保留(n-k)个点的问题,然后先保留标号为n的点,然后以n位根节点dfs建树。
然后再从大到小考虑, 如果要保留第i个点, 第i个点往上走, 走到已经保留的点途经的城市数是不是大于或者等于现在还能保留的城市数目。 如果是就将路上的所有的点都保留下来, 就处理下一个点, 如果数目不够, 就处理下一个点。
因为如果每次往上走k步很容易TLE, 所以我们用倍增法优化一下往上走的过程。然后处理完所有的点得到答案了。
代码:
1 #include<bits/stdc++.h> 2 using namespace std; 3 #define Fopen freopen("_in.txt","r",stdin); freopen("_out.txt","w",stdout); 4 #define LL long long 5 #define ULL unsigned LL 6 #define fi first 7 #define se second 8 #define pb push_back 9 #define lson l,m,rt<<1 10 #define rson m+1,r,rt<<1|1 11 #define max3(a,b,c) max(a,max(b,c)) 12 #define min3(a,b,c) min(a,min(b,c)) 13 typedef pair<int,int> pll; 14 const int INF = 0x3f3f3f3f; 15 const LL mod = 1e9+7; 16 const int N = 1e6+10; 17 vector<int> son[N]; 18 int vis[N]; 19 int anc[N][21]; 20 bool can[N]; 21 void dfs(int o,int u){ 22 for(int i = 0; i < son[u].size(); i++){ 23 int v = son[u][i]; 24 if(o == v) continue; 25 anc[v][0] = u; 26 for(int i = 1; i <= 20; i++) anc[v][i] = anc[anc[v][i-1]][i-1]; 27 dfs(u,v); 28 } 29 } 30 bool check(int x, int k){ 31 for(int i = 20; i >= 0; i--){ 32 if((k >> i)&1) x = anc[x][i]; 33 } 34 return can[x]; 35 } 36 int main(){ 37 int n, u, v, k; 38 scanf("%d%d", &n, &k); 39 for(int i = 1; i < n; i++){ 40 scanf("%d%d", &u, &v); 41 son[u].pb(v); 42 son[v].pb(u); 43 } 44 for(int i = 0; i <= 20; i++) anc[n][i] = n; 45 dfs(n,n); 46 k = n - k - 1; 47 can[n] = 1; 48 for(int i = n - 1; i >= 1; i--){ 49 if(check(i,k)){ 50 int t = i; 51 while(!can[t]){ 52 can[t] = 1; 53 t = anc[t][0]; 54 k--; 55 } 56 } 57 } 58 for(int i = 1; i <= n; i++) 59 if(!can[i]) printf("%d ", i); 60 return 0; 61 }