codeforces 700B. Connecting Universities

貪心

題目傳送門

題目大意: 一棵樹上有2k2k個關鍵點,把這些關鍵點兩兩配對,貢獻爲配對點的距離之和。求最大貢獻。

樹上兩點之間的距離爲dep[x]+dep[y]2dep[lca(x,y)]dep[x]+dep[y]-2*dep[lca(x,y)]。對於這2k2k個點,它們的深度之和是確定的,那麼我們要使儘可能多的lca深度儘量小。

對於一個節點xx,設其子樹中的關鍵點個數爲sz[x]sz[x],其子節點的子樹中最多的關鍵點個數爲mxmx,之前已經配對的點個數爲ss。如果mx2sz[x]+smx*2\leq sz[x]+s,那麼對於xx子樹中所有的關鍵點,一定存在一種配對方案使得它們的LCA都是xx。否則最多會有(sz[x]mx)2(sz[x]-mx)*2個關鍵點被配對(所有點都和mxmx裏的點配對),mxmx中還剩下2mxsz[x]2*mx-sz[x]個關鍵點,這些要累加到ss中並遞歸子樹mxmx

s=2ksz[x]s=2k-sz[x],把上述化簡即若mxkmx\leq k則貢獻爲2dep[x](sz[x]k)2dep[x]*(sz[x]-k),否則爲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;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章