[Codeforces 980E] The Number Games

(emm…懒惰的博主终于开始写题解了,然而怎么还是水题。。)

传送门
题意:给出一颗 N 个节点的树,第 i 个节点的权值为 2 i , 现在你必须删除 K 个节点( K < N ),并且保证剩下的点都联通的情况下,使得剩下的点权值最大。

删除 K 个节点,就是选择 N K 个节点嘛…
看到有 2 i ,应该很容易想到贪心的思路吧…
优先选取 i 大的点,毕竟那些小的就算全部加起来也没这个大。。。
首先,节点 N 肯定要选的啦,然后就以N为根了。
然后 i N 1 枚举到1,因为题目要求联通,那从 i 到根路径上所有未选点肯定都要选了,而且肯定是连续一条链。
那怎么找到这条链上离 i 最远且未选过的点呢。。倍增就好了。。。
emm本来还想画个图..然而谁能给我推荐一下ubuntu下要用什么画啊。。。
而且博主是个连怎么装软件都没完全搞清楚的小白啊。。。。
扯…扯远了。。
贴代码吧

#include<bits/stdc++.h>
using namespace std;
const int N=1000001;
const int M=2000001;
const int Lg=20;
int n,m,Next[M],head[N],v[M],f[N][Lg],b[N],cnt,dep[N];   //b数组标记是否选过,f就是祖先的ST表

inline void read(int &x){
    char ch=getchar();x=0;
    for(;ch<'0'||ch>'9';ch=getchar());
    for(;ch>='0'&&ch<='9';ch=getchar()) x=(x<<3)+(x<<1)+ch-'0';
}

void add(int x,int y){   //挂链存边
    Next[++cnt]=head[x];
    head[x]=cnt;
    v[cnt]=y;
}

void dfs(int x,int fa){
    dep[x]=dep[fa]+1;
    f[x][0]=fa;
    for(int i=head[x];i!=-1;i=Next[i])
     if (v[i]!=fa) dfs(v[i],x);
}

int getf(int x){   //找出这条链上离x最远且未选过的点
    for(int i=Lg-1;i>=0;i--)
     if (!b[f[x][i]]) x=f[x][i];
    return f[x][0];
}

void baoli(int x){    //暴力将路径上的节点标记为选过
    for(;!b[x];x=f[x][0]) b[x]=1;
}

int main(){
    read(n);read(m);
    m=n-m;
    memset(head,-1,sizeof head);
    for(int i=1;i<n;i++){
        int x,y;
        read(x);read(y);
        add(x,y);
        add(y,x);
    }
    dfs(n,0);    //以n为根建树
    for(int i=1;i<Lg;i++)
     for(int j=1;j<=n;j++)
      f[j][i]=f[f[j][i-1]][i-1];   //建ST表用来倍增
    b[n]=1;b[0]=1;m--;
    for(int i=n-1;i;i--)
     if (!b[i]){
        int fa=getf(i);
        if (dep[i]-dep[fa]<=m){
            m-=dep[i]-dep[fa];
            baoli(i);
        }
     }
    for(int i=1;i<=n;i++)
     if (!b[i]) printf("%d ",i);
    return 0;
}

猜你喜欢

转载自blog.csdn.net/ymzqwq/article/details/81157133
今日推荐