[CF592D]Super M

592D:Super M

題意簡述

給出一棵n 個節點的樹,其中有m 個節點爲標記節點。
樹上相鄰節點距離爲1
你被要求從任意點出發,遍歷這些標記節點,求最小的總路程。

數據範圍

1mn123456

思路

樹形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;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章