题意:
点1为根节点。
n,k<=2e5
解法:
比较显然的结论:
如果将一个点变成红色点,那么红色点的父节点就不能被选为蓝色点。
因此红方的最优操作一定是选择叶子节点变成红色。
1. 如果叶子数量sz<=k,那么蓝方就无法操作,
此时枚举红色节点数量[sz,k],计算答案最大值即可。
2. 如果叶子数量sz>k,那么红方选择叶子节点只能贪心的来选:
选择叶子到根节点这条链长度最大的叶子节点。然后将该链删掉。
贪心的选择k次。
问题在于如何快速的找到链最长的节点。
一个简单的做法是先对树进行长链剖分,红方选择最长的k条链即可。
最后枚举蓝色点的数量计算答案。
Code:
#include <bits/stdc++.h>
using namespace std;
#define X first
#define Y second
#define int long long
#define PI pair<int, int>
const int maxm=2e6+5;
// const int mod=998244353;
const int mod=1e9+7;
int n,k;
vector<int>g[maxm];
int mdep[maxm]; // 子节点最大深度
int mson[maxm]; // 最大深度子节点
int dep[maxm]; // 深度
vector<int>len; // 所有链的长度
void add(int x,int y){
g[x].push_back(y);
}
void dfs1(int x,int fa){
mdep[x]=dep[x];
mson[x]=0;
for(int v:g[x]){
if(v==fa)continue;
dep[v]=dep[x]+1;
dfs1(v,x);
mdep[x]=max(mdep[x],dep[v]);
// 维护x的重儿子
if(mson[x]==0||mdep[v]>mdep[mson[x]]){
mson[x]=v;
}
}
}
void dfs2(int x,int fa,int tp){
// x是链头, 将链长度存起来
if(x==tp){
len.push_back(mdep[x]-dep[x]+1);
}
// 先遍历重链
if(mson[x]) {
dfs2(mson[x],x,tp);
}
// 再遍历轻链
for(int v:g[x]){
if(v==fa)continue;
if(v==mson[x])continue;
dfs2(v,x,v);
}
}
void solve() {
cin>>n>>k;
for(int i=1;i<n;i++){
int l,r;cin>>l>>r;
add(l,r);
add(r,l);
}
dfs1(1,1);
dfs2(1,1,1);
sort(len.begin(),len.end());
int sz=len.size();
// 叶子数量sz<=k时, 红方可以在[sz,k]内任选红色点数量
// 而蓝方无法选蓝色点
// 枚举红点数量计算ans最大值
if(sz<=k){
int ans=0;
for(int i=sz;i<=k;i++){
int r=i;
ans=max(ans,(n-r)*r);
}
cout<<ans<<endl;
return ;
}
// 叶子数量>k时,红方贪心的选链长最大的k个叶子
// 蓝方可以对剩余的链任取蓝点
// 枚举蓝色点计算ans最小值
int ans=(n-k)*k;
int mb=0;
for(int i=0;i<sz-k;i++){
mb+=len[i];
}
for(int i=0;i<=mb;i++){
int w=n-k-i;
int r=k;
int b=i;
ans=min(ans,w*(r-b));
}
cout<<ans<<endl;
}
signed main() {
// #define MULTI_CASE
ios::sync_with_stdio(0);
cin.tie(0);
#ifndef ONLINE_JUDGE
freopen("../in.txt", "r", stdin);
freopen("../out.txt", "w", stdout);
#endif
#ifdef MULTI_CASE
int T;
cin >> T;
while (T--)
#endif
solve();
return 0;
}