CF 980E-The Number Games

题目链接

http://codeforces.com/problemset/problem/980/E

题意简述

给你n个点,n<=100w,每个点有点权,点权为 2 i ,再给你一个整数k,k<=100w,表示你要删除k个点后使得这棵树仍联通且剩余点的权值和最大,输出所有删除的点。
乍一看以为树形dp,突然发现k也小于等于100w,好像不行啊。
我们把题意转换为选择n-k个点,这些点联通且权值和最大。
猛然发现一个非常特殊的性质,第i个点的点权为 2 i ,nice啊,这就保证了我们肯定不惜任何代价要将目前最大的点连进来。
所以我们用一个set维护现在还未选择的点,找到当前最大的点,用倍增判一下选择它最少要选择多少个点,(具体实现看代码),如果是小于等于现在还能选的点数res的,那就从他开始暴力往上跳(建树的时候以n为根),把沿路经过的定点都从set里删除,顺便把vis标记一下。如果不够选择,把这个点从set里删掉,继续做就行了。
最后vis没有被标记过的点就是没有选择的点了。

#include<cstdio>
#include<algorithm>
#include<cctype>
#include<cstring>
#include<iostream>
#include<cmath>
#include<set> 
#define LL long long
#define INF (2139062143)
#define N (1000001)
using namespace std;
int n,k,root,x,y,tot,res;
int fa[N][31],a[N<<1],nxt[N<<1],head[N];
bool vis[N];
set <int> S;
set <int>::iterator Pos;
inline void add(int x,int y){
    a[++tot]=y,nxt[tot]=head[x],head[x]=tot;
}
template <typename T> void read(T&t) {
    t=0;
    bool fl=true;
    char p=getchar();
    while (!isdigit(p)) {
        if (p=='-') fl=false;
        p=getchar();
    }
    do {
        (t*=10)+=p-48;p=getchar();
    }while (isdigit(p));
    if (!fl) t=-t;
}
void dfs(int u){
    for (int p=head[u];~p;p=nxt[p]){
        if (a[p]!=fa[u][0]){
            fa[a[p]][0]=u;
            dfs(a[p]);
        }
    }
}
int main(){
    read(n),read(k);
    memset(head,-1,sizeof(head));
    for (int i=1;i<n;i++){
        S.insert(i);
        read(x),read(y);
        add(x,y);
        add(y,x);
    }
    dfs(n);
    for (int j=1;j<=30;j++){
        for (int i=1;i<=n;i++)
        fa[i][j]=fa[fa[i][j-1]][j-1];
    }
    if (k==n){
        for (int i=1;i<=n;i++) printf("%d ",i);
        return 0;
    }
    vis[n]=1;
    vis[0]=1;
    res=n-k-1;
    while (res>0){
        Pos=S.end();
        Pos--;
        int k=*Pos,tt=0,now=k;
        for (int i=30;i>=0;i--){
            if (!vis[fa[now][i]]){
                now=fa[now][i];
                tt+=1<<i;
            }   
        }
        tt++;
        //printf("%d %d\n",k,tt);
        if (tt<=res){
            int now=k;
            while (!vis[now]){
                vis[now]=1;
                res--;
                S.erase(now);
                now=fa[now][0];
            }
        }
        else{
            S.erase(Pos);
        }
    }
    for (int i=1;i<=n;i++){
        if (!vis[i]) printf("%d ",i);
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_36056315/article/details/80252893
今日推荐