题意:一棵树有n个点,边长都为1,在这n个点中有2*k个大学,这些大学不重复。现在要给这些大学两两牵网线,问需要网线的最大长度。
分析:考虑每条边对答案的贡献,每条边对答案的贡献就是他的子树上学校的点的数目和剩下的学校的数目的较小值。
具体做法就是,一次dfs,记录下此点的子节点中有多少个学校(包括它自身),那么与它父亲节点相连的边的贡献为min(cnt[id],2*k-cnt[id])(cnt[id]!=0)。
这道题和2016多校1的:hdu5723类似 点击打开链接
#include<iostream>
#include<string>
#include<stdio.h>
#include<string.h>
#include<vector>
#include<math.h>
#include<queue>
#include<map>
#include<set>
#include<algorithm>
using namespace std;
#define MAXN 200005
#define LL long long
#define INF 0x7f7f7f7f
const double eps = 1e-10;
struct edge
{
LL en,next;
}Edge[MAXN*2];
LL p[MAXN],num,ans,k;
LL cnt[MAXN],vis[MAXN],flag[MAXN];
void init()
{
memset(p,-1,sizeof(p));
num=0;
}
void add(LL st,LL en)
{
Edge[num].en=en;
Edge[num].next=p[st];
p[st]=num++;
}
void dfs(LL id)
{
vis[id]=1;
for(LL i=p[id];i!=-1;i=Edge[i].next)
{
LL en=Edge[i].en;
if(!vis[en])
{
dfs(en);
cnt[id]+=cnt[en];
if(cnt[en]!=0)
{
ans+=min(2*k-cnt[en],cnt[en]);
}
}
}
if(flag[id])
cnt[id]++;
}
int main()
{
LL n,i;
while(scanf("%I64d%I64d",&n,&k)!=EOF)
{
init();
memset(flag,0,sizeof(flag));
for(i=1;i<=2*k;i++)
{
LL id;
scanf("%I64d",&id);
flag[id]=1;
}
for(i=1;i<=n-1;i++)
{
LL u,v;
scanf("%I64d%I64d",&u,&v);
add(u,v);
add(v,u);
}
memset(vis,0,sizeof(vis));
memset(cnt,0,sizeof(cnt));
ans=0;
dfs(1);
printf("%I64d\n",ans);
}
return 0;
}