1. 程式人生 > >【NOIP模擬】怪獸

【NOIP模擬】怪獸

題面

大 M 是一隻怪獸,準備到位元王國吃人。位元王國有 n 個城市,城市之間由 n-1 條無向的路徑連線,通過每條路徑的時間為 1。其中有 m 個特別的城市,這 m 個 城市裡都各有一個大神,於是大 M 打算不管普通人,只吃掉這些大神。然而大 M 是 一隻具有特別能力的怪物,它可以一開始降臨到 n 個城市中的任意一個城市,同時還 有一次機會在任意兩個城市間開啟一個蟲洞,不消耗時間就能相互到達。 大 M 想知道它最少要花多少時間來吃掉這些大神(吃的時間忽略不計),如 果你不幫它它就會吃了你。 當然,大 M 是不屬於這個時空的存在,所以在他吃完所有大神之後需要回到 最初降臨的城市,通過時空門返回原來的位面。蟲洞只能走一次。

第 1 行輸出一個整數表示為了時間最短,大 M 一開始應該降臨在哪個城市 (如果有多個最短時間則輸出序號最小的城市編號)。 第 2 行輸出一個整數表示能達到的最短時間。1<=m<=n<=123456。

分析

這題其實挺妙,按一般樹形dp的思維能做,但是這一種思路更妙。

首先很顯然,所有標記了的大神點的父親必須走,把這些點標記出來,再把一條直路上的父親結點略去,之間將最遠的祖先與大神點的邊權設為中間經過的邊數。

如下圖,再這樣一棵生成的樹上的所有點都必須走,且從任意一點出發都是等效的,這解決了我們第一個問題,直接生成樹上找一個序號最小的點。

而代價呢?其實就是邊權之和*2-樹的直徑,因為有了要回到起點的條件,所以邊都要走兩遍,而蟲洞自然建在距離最遠的兩點,即直徑上兩點。

程式碼

#include<bits/stdc++.h>
using namespace std;
#define N 200000
int n,m,k,p,st,cnt,cot,dis,ans;
int d[N],mark[N],siz[N],first[N],head[N];
struct email
{
    int u,v,w;
    int nxt;
}e[N*2],g[N*2];
inline void add(int u,int v)
{
    e[++cnt].nxt=first[u];first[u]=cnt;
    e[cnt].u
=u;e[cnt].v=v; } inline void readd(int u,int v,int w) { g[++cot].nxt=head[u];head[u]=cot; g[cot].u=u;g[cot].v=v;g[cot].w=w; } void dfs(int u,int fa) { siz[u]=mark[u]; for(int i=first[u];i;i=e[i].nxt) { int v=e[i].v; if(v==fa)continue; dfs(v,u); siz[u]+=siz[v]; } } void dfs1(int u,int fa,int top,int w) { int dalao=0; for(int i=first[u];i;i=e[i].nxt) { int v=e[i].v; if(v==fa)continue; if(siz[v])dalao++; } if(dalao>1||mark[u]) { if(top)readd(u,top,w),readd(top,u,w); top=u;w=0; } for(int i=first[u];i;i=e[i].nxt) { int v=e[i].v; if(v==fa||siz[v]==0)continue; dfs1(v,u,top,w+1); } } inline void dfs2(int u,int fa) { for(int i=head[u];i;i=g[i].nxt) { int v=g[i].v,w=g[i].w; if(v==fa)continue; d[v]=d[u]+w; dfs2(v,u); } } int main() { scanf("%d%d",&n,&m); for(int i=1;i<n;i++) { int u,v; scanf("%d%d",&u,&v); add(u,v);add(v,u); } for(int i=1;i<=m;i++)scanf("%d",&k),mark[k]=1; st=n;dfs(1,0);dfs1(1,0,0,0); dfs2(k,0); for(int i=1;i<=n;i++) if(d[i]>dis)dis=d[i],p=i; memset(d,0,sizeof(d));dfs2(p,0); for(int i=1;i<=n;i++) dis=max(dis,d[i]); for(int i=1;i<=cot;i++)st=min(st,min(g[i].v,g[i].u)),ans+=g[i].w; printf("%d\n%d\n",st,ans-dis); return 0; }