CF 980E - The Number Games 贪心 倍增

题意:

给你一颗无根树,每个点的权值是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;
}

猜你喜欢

转载自blog.csdn.net/c6376315qqso/article/details/82466350
今日推荐