貪心
題目大意: 一棵樹上有個關鍵點,把這些關鍵點兩兩配對,貢獻爲配對點的距離之和。求最大貢獻。
樹上兩點之間的距離爲。對於這個點,它們的深度之和是確定的,那麼我們要使儘可能多的lca深度儘量小。
對於一個節點,設其子樹中的關鍵點個數爲,其子節點的子樹中最多的關鍵點個數爲,之前已經配對的點個數爲。如果,那麼對於子樹中所有的關鍵點,一定存在一種配對方案使得它們的LCA都是。否則最多會有個關鍵點被配對(所有點都和裏的點配對),中還剩下個關鍵點,這些要累加到中並遞歸子樹。
而,把上述化簡即若則貢獻爲,否則爲。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;
}