[APIO2010]巡邏-樹的直徑
題目描述
在一個地區中有 n 個村莊,編號為 1, 2, ..., n。有 n – 1 條道路連線著這些村 莊,每條道路剛好連線兩個村莊,從任何一個村莊,都可以通過這些道路到達其 他任一個村莊。每條道路的長度均為 1 個單位。 為保證該地區的安全,巡警車每天要到所有的道路上巡邏。警察局設在編號 為 1 的村莊裡,每天巡警車總是從警察局出發,最終又回到警察局。 下圖表示一個有 8 個村莊的地區,其中村莊用圓表示(其中村莊 1 用黑色的 圓表示),道路是連線這些圓的線段。為了遍歷所有的道路,巡警車需要走的距 離為 14 個單位,每條道路都需要經過兩次。
為了減少總的巡邏距離,該地區準備在這些村莊之間建立 K 條新的道路, 每條新道路可以連線任意兩個村莊。兩條新道路可以在同一個村莊會合或結束 (見下面的圖例(c))。 一條新道路甚至可以是一個環,即,其兩端連線到同一 個村莊。 由於資金有限,K 只能是 1 或 2。同時,為了不浪費資金,每天巡警車必須 經過新建的道路正好一次。 下圖給出了一些建立新道路的例子:
在(a)中,新建了一條道路,總的距離是 11。在(b)中,新建了兩條道路,總 的巡邏距離是 10。在(c)中,新建了兩條道路,但由於巡警車要經過每條新道路 正好一次,總的距離變為了 15。 試編寫一個程式,讀取村莊間道路的資訊和需要新建的道路數,計算出最佳 的新建道路的方案使得總的巡邏距離最小,並輸出這個最小的巡邏距離。
輸入輸出格式
輸入格式:
第一行包含兩個整數 n, K(1 ≤ K ≤ 2)。接下來 n – 1 行,每行兩個整數 a, b, 表示村莊 a 與 b 之間有一條道路(1 ≤ a, b ≤ n)。
輸出格式:
輸出一個整數,表示新建了 K 條道路後能達到的最小巡邏距離。
輸入輸出樣例
輸入樣例#1:8 1 1 2 3 1 3 4 5 3 7 5 8 5 5 6輸出樣例#1:
11
8 2 1 2 3 1 3 4 5 3 7 5 8 5 5 6輸出樣例#2:
10輸入樣例#3:
5 2 1 2 2 3 3 4 4 5輸出樣例#3:
6
說明
10%的資料中,n ≤ 1000, K = 1;
30%的資料中,K = 1;
80%的資料中,每個村莊相鄰的村莊數不超過 25;
90%的資料中,每個村莊相鄰的村莊數不超過 150;
100%的資料中,3 ≤ n ≤ 100,000, 1 ≤ K ≤ 2。
思路:
當k=1時,這道題只需要求樹的直徑(即樹上的最長路徑),相當於省掉了樹的直徑-1(新建的道路需走一次)條路徑,答案為(n-1)<<1-樹的直徑+1
樹的直徑的求法不詳細說明了,Luogu的第二位dalao的題解十二分的透徹(https://www.luogu.org/problemnew/solution/P3629)
然後當k=2時,你會發現求兩個樹的直徑可能會有覆蓋的情況,所以你只需要將這條路的值由1改為-1(如果選擇被兩個環都覆蓋路徑,則他在第一個環內無法減少路徑,第二個環亦然,故需要-1(即路徑+2))
然後就在求一個樹的直徑就好了(如果兩個環由重疊也已處理完畢)
不管這個直徑需要用樹狀DP來做(dfs不能處理負權邊)
上程式碼
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #define rep(i,a,b) for(long long i=a;i<=b;i++) using namespace std; typedef long long ll; ll n,k,en,to[200500],tot,val[200500],nxt[200500],fir[100500],maxn,D1,st,dis[200500]; bool book[200050]; void ade(ll u,ll v){ to[++tot]=v; nxt[tot]=fir[u]; fir[u]=tot; val[tot]=1; } void dfs(ll x,ll fa,ll s){ if(s>maxn){maxn=s; en=x;} for(ll k=fir[x];k;k=nxt[k]) if(to[k]!=fa) dfs(to[k],x,s+1); } bool dfs2(ll x,ll fa,ll s){ if(s==D1 && x==en){book[x]=1; return 1;} for(ll k=fir[x];k;k=nxt[k]) if(to[k]!=fa) if(dfs2(to[k],x,s+1)){book[x]=1; return 1;} return 0; } void Tree_DP(ll x,ll fa){ //樹上求最長鏈 for(ll k=fir[x];k;k=nxt[k]){ if(to[k]!=fa){ Tree_DP(to[k],x); maxn=max(maxn,dis[x]+dis[to[k]]+val[k]); dis[x]=max(dis[x],dis[to[k]]+val[k]); } } } int main(){ scanf("%lld%lld",&n,&k); rep(i,1,n-1){ ll u,v; scanf("%lld%lld",&u,&v); ade(u,v);ade(v,u); } dfs(1,0,0); maxn=0; st=en; dfs(st,0,0); D1=maxn; if(k==1){ printf("%lld",((n-1)<<1)-D1+1); return 0; } book[0]=dfs2(st,0,0); rep(i,1,n) if(book[i]) for(ll k=fir[i];k;k=nxt[k]) if(book[to[k]]) val[k]=-1; maxn=0; Tree_DP(1,0); printf("%lld\n",(n<<1)-D1-maxn); return 0; }