先考虑最小值,因为树是二分图,所以可以进行黑白染色,将其分成左右部图,让左部图向右部图连边即可构造最小值,为 \(n-1\)。
对于最大值,最优情况一定是以一个点为中心,其各个子树内边的状态为要么该点能到达子树内的每个点,即外向,要么子树内的每个点能到达该点,即内向,且该点为树的重心。
可以简单的进行证明。考虑边的状态让这棵树有了两个上面描述的中心,两个中心之间的路径上一半的边指向第一个中心,一半的边指向第二个中心。发现若这条路径边的方向只指向一个中心,答案会更优,所以就将这两个中心合并为一个,同理,若存在多个中心,合并为一个会更优。因为中心的各个子树的外向个数和内向个数会乘起来贡献给答案,所以中心选取树的重心可以更好的分配外向内向的子树大小。
考虑将中心的各个子树分为外向和内向两个集合,两个集合大小越接近 \(\frac{n}{2}\) 越优。可以通过二进制拆分来优化这个多重背包问题,因为只用考虑存在性,所以也采用 \(bitset\) 来优化。
\(code:\)
#include<bits/stdc++.h>
#define maxn 500010
using namespace std;
typedef long long ll;
template<typename T> inline void read(T &x)
{
x=0;char c=getchar();bool flag=false;
while(!isdigit(c)){if(c=='-')flag=true;c=getchar();}
while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=getchar();}
if(flag)x=-x;
}
int n,root;
ll ans;
int siz[maxn],ma[maxn],cnt[maxn];
bitset<maxn> s;
struct edge
{
int to,nxt;
}e[maxn];
int head[maxn],edge_cnt;
void add(int from,int to)
{
e[++edge_cnt]=(edge){to,head[from]};
head[from]=edge_cnt;
}
void dfs(int x,int fa)
{
siz[x]=1;
for(int i=head[x];i;i=e[i].nxt)
{
int y=e[i].to;
if(y==fa) continue;
dfs(y,x),siz[x]+=siz[y];
ma[x]=max(ma[x],siz[y]);
}
ma[x]=max(ma[x],n-siz[x]);
if(ma[x]<ma[root]) root=x;
}
int main()
{
read(n),ma[0]=n;
for(int i=1;i<n;++i)
{
int x,y;
read(x),read(y);
add(x,y),add(y,x);
}
dfs(1,0),dfs(root,0);
for(int i=1;i<=n;++i) ans+=siz[i]-1;
for(int i=head[root];i;i=e[i].nxt) cnt[siz[e[i].to]]++;
for(int i=1;i<=n;++i)
while(cnt[i]>2)
cnt[i]-=2,cnt[i*2]++;
s[0]=1;
for(int i=1;i<=n;++i)
for(int j=1;j<=cnt[i];++j)
s|=s<<i;
for(int i=n/2;i;--i)
{
if(!s[i]) continue;
printf("%d %lld\n",n-1,ans+(ll)i*(n-i-1));
break;
}
return 0;
}