原题链接:
https://codeforces.com/contest/1675/problem/F
题目和18946 小美的送花线路 有相似之处。小美送花,只有把最远的结点最后送达才能用时最短。
题目大意:一个树结构,x是树根,要求先访问k个点(可能重复),最后访问y。
解题思路:树结构问题并不属于第五章内容,而是被看成特殊的图结构。在树结构中,如果要访问的两个点在树根的不同分支,那么(1)必须先访问一个分支,(2)返回树根后,(3)再访问另一个分支。这样第一个分支一去一回,每条边会访问两次。而最后访问的点因为无需返回树根,所以每条边只走一次。
此题目可以把y和其他k个点等同看待,只是必须把y作为最后一个访问的结点。这样答案= 所有路径长度乘以2 - y结点的深度。因为到y之后不用再返回树根了。
在dfs过程中,检查这条边是否连接任务结点,是的话就必须有去有回,总长度+2。
图的存储方法用的是邻接表写法,可参看 19085 图的存储结构(邻接表)
#include <bits/stdc++.h>
typedef long long ll;
using namespace std;
int t,n,k,v[200005],x,y,ans,d[200005];/**< v数组标记任务点,d数组记录深度,其实只需要记录y深度 */
vector<int>e[200005];
int dfs(int root,int deep,int fa)
{
d[root]=deep;/**< 只是记录深度 */
int knum=v[root];
for(int i=0; i<e[root].size(); i++)
if(e[root][i]!=fa&&dfs(e[root][i],deep+1,root))/**< 如果dfs返回值不为0,说明这条边<root,e[root][i]>必须走 */
ans+=2,knum++;/**< knum记录任务结点数量,并作为函数值返回,供上层结点判定边是否要走 */
return knum;
}
int main()
{
ios::sync_with_stdio(0),cin.tie(0);
int i,j,temp,A,B;
cin>>t;
while(t--)
{
cin>>n>>k>>x>>y;
ans=0;
for(i=1; i<=n; i++)
v[i]=d[i]=0,e[i].clear();
for(i=1; i<=k; i++)
{
cin>>temp;
v[temp]=1;
}
v[y]=1;/**< 把y也看成是任务点,但是这个点一定是最后访问 */
for(i=1; i<n; i++)
{
cin>>A>>B;
e[A].push_back(B);
e[B].push_back(A);
}
dfs(x,0,0);
cout<<ans-d[y]<<endl;/**< 走的总路径长度-y的深度 */
}
return 0;
}