题意:
从一颗 (n) 个节点的树上,选择 (k) 个点,要求从这些点到根节点 (1) 的路径上经过的非选中的点的个数的总和最大。
数据范围:(2≤n≤2⋅10^5, 1≤k<n)
传送门
分析:
从深度和子树大小去考虑。
首先,基于贪心的策略,优先选择深度大的点。对于一个点,如果选择该点,那么其子树中的所有点肯定均被选择了。考虑选择该点会最后的总和产生什么影响,首先会加上该点的深度,然后子树中所有点的贡献都会 (-1),即 (ans+=(depth[v]-sz[v]+1))。那么我们把该值排个序,取最大的 (k) 个就行了。
反思:想到了点深度和子树大小,但没有把它们联系在一起考虑。
代码:
#include <bits/stdc++.h>
#define pb push_back
using namespace std;
typedef long long ll;
const int N=2e5+5;
vector<int>G[N];
int sz[N],a[N];
bool cmp(int x,int y)
{
return x>y;
}
void dfs(int v,int p,int d)
{
sz[v]=1;
for(int i=0;i<G[v].size();i++)
{
int u=G[v][i];
if(u==p)
continue;
dfs(u,v,d+1);
sz[v]+=sz[u];
}
a[v]=d-sz[v];
}
int main()
{
int n,k,v,u;
ll ans=0;
scanf("%d%d",&n,&k);
for(int i=1;i<n;i++)
{
scanf("%d%d",&u,&v);
G[u].pb(v);
G[v].pb(u);
}
dfs(1,1,1);
sort(a+1,a+1+n,cmp);
for(int i=1;i<=k;i++)
ans+=a[i];
printf("%lld
",ans);
return 0;
}