贪心
题目大意: 一棵树上有 2 k 2k 2k个关键点,把这些关键点两两配对,贡献为配对点的距离之和。求最大贡献。
树上两点之间的距离为 d e p [ x ] + d e p [ y ] − 2 ∗ d e p [ l c a ( x , y ) ] dep[x]+dep[y]-2*dep[lca(x,y)] dep[x]+dep[y]−2∗dep[lca(x,y)]。对于这 2 k 2k 2k个点,它们的深度之和是确定的,那么我们要使尽可能多的lca深度尽量小。
对于一个节点 x x x,设其子树中的关键点个数为 s z [ x ] sz[x] sz[x],其子节点的子树中最多的关键点个数为 m x mx mx,之前已经配对的点个数为 s s s。如果 m x ∗ 2 ≤ s z [ x ] + s mx*2\leq sz[x]+s mx∗2≤sz[x]+s,那么对于 x x x子树中所有的关键点,一定存在一种配对方案使得它们的LCA都是 x x x。否则最多会有 ( s z [ x ] − m x ) ∗ 2 (sz[x]-mx)*2 (sz[x]−mx)∗2个关键点被配对(所有点都和 m x mx mx里的点配对), m x mx mx中还剩下 2 ∗ m x − s z [ x ] 2*mx-sz[x] 2∗mx−sz[x]个关键点,这些要累加到 s s s中并递归子树 m x mx mx。
而 s = 2 k − s z [ x ] s=2k-sz[x] s=2k−sz[x],把上述化简即若 m x ≤ k mx\leq k mx≤k则贡献为 2 d e p [ x ] ∗ ( s z [ x ] − k ) 2dep[x]*(sz[x]-k) 2dep[x]∗(sz[x]−k),否则为 2 d e p [ x ] ∗ ( s z [ x ] − m x ) 2dep[x]*(sz[x]-mx) 2dep[x]∗(sz[x]−mx)。DFS算一遍就好了。
代码:
#include<cctype>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define N 200005
#define F inline
using namespace std;
typedef long long LL;
struct edge{ int nxt,to; }ed[N<<1];
int n,m,k,a[N],h[N],sz[N],fa[N],dep[N];
bool f[N]; LL ans;
F char readc(){
static char buf[100000],*l=buf,*r=buf;
if (l==r) r=(l=buf)+fread(buf,1,100000,stdin);
return l==r?EOF:*l++;
}
F int _read(){
int x=0; char ch=readc();
while (!isdigit(ch)) ch=readc();
while (isdigit(ch)) x=(x<<3)+(x<<1)+(ch^48),ch=readc();
return x;
}
void dfs1(int x){
dep[x]=dep[fa[x]]+1,sz[x]=f[x];
for (int i=h[x],v;i;i=ed[i].nxt)
if ((v=ed[i].to)!=fa[x]) fa[v]=x,dfs1(v),sz[x]+=sz[v];
}
void dfs2(int x){
int mx=0,id;
for (int i=h[x],v;i;i=ed[i].nxt)
if ((v=ed[i].to)!=fa[x]&&mx<sz[v]) mx=sz[v],id=v;
if (mx<<1<=sz[1])
return void(ans-=1ll*dep[x]*(2*sz[x]-sz[1]));
ans-=1ll*(sz[x]-mx)*dep[x]<<1,dfs2(id);
}
#define add(x,y) ed[++k]=(edge){h[x],y},h[x]=k
int main(){
n=_read(),m=_read()<<1;
for (int i=1;i<=m;i++) f[a[i]=_read()]=true;
for (int i=1,x,y;i<n;i++)
x=_read(),y=_read(),add(x,y),add(y,x);
dfs1(1),dfs2(1);
for (int i=1;i<=m;i++) ans+=dep[a[i]];
return printf("%lld",ans),0;
}