(emm…懒惰的博主终于开始写题解了,然而怎么还是水题。。)
传送门
题意:给出一颗
个节点的树,第
个节点的权值为
, 现在你必须删除
个节点(
),并且保证剩下的点都联通的情况下,使得剩下的点权值最大。
删除
个节点,就是选择
个节点嘛…
看到有
,应该很容易想到贪心的思路吧…
优先选取
大的点,毕竟那些小的就算全部加起来也没这个大。。。
首先,节点
肯定要选的啦,然后就以N为根了。
然后
从
枚举到1,因为题目要求联通,那从
到根路径上所有未选点肯定都要选了,而且肯定是连续一条链。
那怎么找到这条链上离
最远且未选过的点呢。。倍增就好了。。。
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;
}