592D:Super M
题意简述
给出一棵
n
个节点的树,其中有
树上相邻节点距离为
1
。
你被要求从任意点出发,遍历这些标记节点,求最小的总路程。
数据范围
思路
树形DP。
最优的方案肯定是沿着DFS序走。
每DFS到一个标记节点,就把沿路往上的路径算两遍加入贡献直到另一个标记节点。
因为我们不必回到起点,所有再做一次DFS,求出标记节点之间的最远距离,减到答案里。
这一步我们可以用类似树的直径的方法DFS两次。
代码
#include<cstdio>
#include<cstring>
#include<iostream>
using namespace std;
struct Node{
int s,t,next;
}e[250010];
int head[130010],cnt;
void addedge(int s,int t)
{
e[cnt].s=s;e[cnt].t=t;e[cnt].next=head[s];head[s]=cnt++;
e[cnt].s=t;e[cnt].t=s;e[cnt].next=head[t];head[t]=cnt++;
}
bool pd[130010],lable[130010];
int dis[130010];
int n,m,u,v,pos,ans,tmp;
void dfs(int node,int lastfa)
{
pd[node]=lable[node];
for (int i=head[node];i!=-1;i=e[i].next)
if (e[i].t!=lastfa)
{
dfs(e[i].t,node);
if (pd[e[i].t])
ans+=2;
pd[node]|=pd[e[i].t];
}
}
void dfs2(int node,int lastfa,int sum)
{
dis[node]=sum;
for (int i=head[node];i!=-1;i=e[i].next)
if (e[i].t!=lastfa)
dfs2(e[i].t,node,sum+1);
}
int main()
{
scanf("%d%d",&n,&m);
memset(head,0xff,sizeof(head));
cnt=0;
for (int i=1;i<n;i++)
{
scanf("%d%d",&u,&v);
addedge(u,v);
}
for (int i=1;i<=m;i++)
{
scanf("%d",&u);
lable[u]=1;
}
dfs(u,u);
dis[0]=-1;
dfs2(u,u,0);
for (int i=1;i<=n;i++)
if (lable[i]&&dis[i]>dis[pos])
pos=i;
tmp=pos;
dfs2(pos,pos,0);
pos=0;
for (int i=1;i<=n;i++)
if (lable[i]&&dis[i]>dis[pos])
pos=i;
ans=ans-dis[pos];
pos=min(pos,tmp);
printf("%d\n%d\n",pos,ans);
return 0;
}