题解:The Number Games

题意分析

给定一棵 \(n\) 个节点的树,第 \(i\) 个节点的权值为 \(2^i\)。从中删除 \(k\) 个节点,使得剩下 \(n-k\) 个节点联通且权值和最大。

求删除哪 \(k\) 个节点。

贪心性质

权值为 \(2^i\) 毫无疑问是极其特殊的,因此我们可以考虑有没有一些性质。

显然,\(2^x>2^{x-1}+2^{x-2}+2^{x-3}+\cdots+2^{0}\),因此我们可以考虑贪心。

即:因为需要使权值和最大,因此让删除的 \(k\) 个点的权值和尽可能小,所以让这 \(k\) 个点的编号尽可能小。

增加点

删除点不好做。

因此我们可以尝试转换为增加点——若确定了剩下 \(n-k\) 个点,也即确定了删除的 \(k\) 个点。

那么我们就需要贪心地使增加的点的编号尽可能大,则 \(n\) 号点肯定是需要保留的。

\(n\) 为根节点建立一棵有根树,加入节点 \(i\) 即将 \(i\)\(n\) 的路径上所有的点加入。

贪心从大致小加入节点即可。

维护路径上尚未加入的点的数量

这样,我们才能判断当前节点 \(i\) 能否加入(如果加入后有根树内的节点数大于 \(n-k\) 则不符合条件)。

考虑暴力的思路,一个一个节点向上跳,跳到已经加入有根树的节点时停止,跳的过程中计数即可。

那么我们优化这样的思路,使用倍增 \(\mathcal O(\log n)\) 向上跳,跳的同时计数即可。

AC 代码

时间复杂度:\(\mathcal O(n\log n)\)

//#include<bits/stdc++.h>
#include<algorithm>
#include<iostream>
#include<cstring>
#include<iomanip>
#include<cstdio>
#include<string>
#include<vector>
#include<cmath>
#include<ctime>
#include<deque>
#include<queue>
#include<stack>
#include<list>
using namespace std;
constexpr const int N=1e6;
struct graph{
	struct edge{
		int v,r;
	}a[N-1<<1|1];
	int size,h[N+1];
	void create(int u,int v){
		a[++size]={v,h[u]};
		h[u]=size;
	}
}g;
int n,k,f[N+1][__lg(N+1)+1];
bool flag[N+1];
void dfs(int x,int fx){
	f[x][0]=fx;
	for(int i=g.h[x];i;i=g.a[i].r){
		int &v=g.a[i].v;
		if(v==fx){
			continue;
		}
		dfs(v,x);
	}
}
void pre(){
	dfs(n,0);
	k--;
	flag[n]=true;
	for(int i=1;(1<<i)<=n;i++){
		for(int x=1;x<=n;x++){
			f[x][i]=f[f[x][i-1]][i-1];
		}
	}
}
int query(int x){
	int ans=1;
	for(int i=__lg(n);i>=0;i--){
		if(!f[x][i]){
			continue;
		}
		if(flag[f[x][i]]){
			continue;
		}
		ans+=(1<<i);
		x=f[x][i];
	}
	return ans;
}
int main(){
	/*freopen("test.in","r",stdin);
	freopen("test.out","w",stdout);*/
	
	ios::sync_with_stdio(false);
	cin.tie(0);cout.tie(0);
	
	cin>>n>>k;
	k=n-k;//注意k此时表示有根树能加入的节点数
	for(int i=1;i<n;i++){
		int u,v;
		cin>>u>>v;
		g.create(u,v);g.create(v,u);
	}
	pre();
	vector<int>ans;
	for(int i=n-1;i>=1;i--){
		if(flag[i]){
			continue;
		}
		int pl=query(i);
		if(pl<=k){
			k-=pl;
			int x=i;
			while(!flag[x]){
				flag[x]=true;
				x=f[x][0];
			}
		}else{
			ans.push_back(i);
		}
	}
	for(int i=ans.size()-1;i>=0;i--){
		cout<<ans[i]<<' ';
	}
	cout.flush();
	
	/*fclose(stdin);
	fclose(stdout);*/
	return 0;
}
posted @ 2025-07-21 21:41  TH911  阅读(7)  评论(0)    收藏  举报