题意:
给你一颗无根树,每个点的权值是2^i,你要从中删除k个点,使得最后剩下的权值最大,并且是联通的。
题解:
反方向考虑保留n - k个点。
由于保留一个i要比保留所有小于i的节点的和都大,所以我们贪心的保留n, n-1, n-2.....
我们以n为根节点建树,然后从n - 1遍历到1, 对于节点i,如果他到已经保留的节点的距离小于剩下可以保留的点数,则保留他们,否则跳过。这样我们需要倍增来找到第一个被选的祖先。
代码:
#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <queue>
#include <bitset>
#include <map>
#include <vector>
#include <stack>
#include <set>
#include <cmath>
#ifdef LOCAL
#define debug(x) cout<<#x<<" = "<<(x)<<endl;
#else
#define debug(x) 1;
#endif
#define chmax(x,y) x=max(x,y)
#define chmin(x,y) x=min(x,y)
#define lson id<<1,l,mid
#define rson id<<1|1,mid+1,r
#define lowbit(x) x&-x
#define mp make_pair
#define pb push_back
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<ll, int> pii;
const ll MOD = 1e9 + 7;
const double eps = 1e-10;
const int INF = 0x3f3f3f3f;
const ll INFLL = 0x3f3f3f3f3f3f3f3fll;
const int MAXN = 1e6 + 5;
vector<int> G[MAXN];
int fa[MAXN][22];
int vis[MAXN];
void dfs (int now, int par) {
fa[now][0] = par;
for (int i = 1; i <= 20; i++) fa[now][i] = fa[fa[now][i - 1]][i - 1];
for(int i : G[now]) {
if(i == par) continue;
dfs(i, now);
}
}
int getpre(int now) {
int len = 0;
for(int i = 20; i >= 0; i--) {
if(!vis[fa[now][i]]) {
now = fa[now][i];
len += (1<<i);
}
}
return len;
}
int main() {
#ifdef LOCAL
freopen ("input.txt", "r", stdin);
#endif
int n, k;
scanf ("%d %d", &n, &k);
for (int i = 1; i < n; i++) {
int x, y;
scanf ("%d %d", &x, &y);
G[x].pb (y);
G[y].pb (x);
}
vis[0] = 1;
dfs (n, 0);
vis[n] = 1;
int ans = 0;
k = n - k - 1;
for(int i = n; i >= 1; i--) {
if(vis[i]) continue;
int len = getpre(i) + 1;
if(len <= k) {
k -= len;
int st = i;
for(int j = 0; j <= len; j++) {
vis[st] = 1;
st = fa[st][0];
}
}
}
for(int i = 1; i <= n; i++) if(!vis[i]) printf("%d ", i);
return 0;
}