题
给定 n 个节点 n-1 条边组成的树,以节点 1 为根。现需要选出 k 个节点作为工业城市,其余城市均为旅游城市。问从所有工业城市出发走到根节点所经过的旅游城市数量之和的最大值。
思路
选出 n-k 个旅游城市
以节点 1 为树根->对于每个节点,我们可以计算出以这个节点为根的子树中包含的节点数量以及这个节点拥有的祖先节点数量。
选择的旅游城市都是尽可能靠近根节点的,首先肯定是将根节点选成旅游城市,then选择某个节点 i 作为旅游城市,那么对答案的“增加的贡献”就是以节点 i 为根的子树节点数量。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
vector<int> G[200050];
bool vis[200050];
struct node
{
int cnt,dis; //cnt存子树除该节点外的节点数量,dis存节点深度
bool operator < (const node& a) const
{
return cnt-dis>a.cnt-a.dis; //按照对答案的贡献(cnt-dis)从大到小排序
}
}ar[200050];
int dfs(int pos,int fa)
{
vis[pos]=true; //标记访问
ar[pos].dis=ar[fa].dis+1; //节点深度=父节点节点深度+1
for(int it:G[pos])
if(!vis[it])
ar[pos].cnt+=dfs(it,pos); //求除自己以外子树中节点数量之和
return ar[pos].cnt+1; //返回时要加上自己
}
int main()
{
ios::sync_with_stdio(0);
cin.tie(0);cout.tie(0);
int n,k,a,b;
cin>>n>>k;
for(int i=1;i<n;i++)
{
cin>>a>>b;
G[a].push_back(b);
G[b].push_back(a);
}
ar[0].dis=-1; //特殊处理下,dis[1]=dis[0]+1=0 -> dis[0]=-1
dfs(1,0);
sort(ar+1,ar+n+1);
ll ans=0;
for(int i=1;i<=n-k;i++)
ans+=ar[i].cnt-ar[i].dis;
cout<<ans<<'\n';
return 0;
}