題目在這裡
這是乙個紫題,當然很難。
我們往簡單的想,不建立新的道路時,從1號節點出發,把整棵樹上的每條邊遍歷至少一次,再回到1號節點,會恰好經過每條邊兩次,路線總長度為$2(n-1)$,根據樹的深度優先遍歷思想,很容易證明這個結論,因為每條邊必然被遞迴一次,回溯一次。
建立1條新道路之後,因為新道路必須恰好經過一次(0次,2次都不可以),所以在沿著新道路(x,y)巡邏之後,要返回x,就必須沿著樹上從y到x的路徑巡邏一遍,最終形成乙個環。與不建立新道路的情況相結合,相當於樹上x與y之間的路徑就只需經過一次了。
因此,當$k=1$時,我們找到樹的最長鏈,在兩個端點之間加一條新道路,就能讓總的巡邏距離最小。若樹的直徑為l,答案就是$2(n-1)-l+1$。
建立第2條新道路(u,v)之後,又會形成乙個環。若兩條新道路形成的環不重疊,則樹上u,v之間的路徑只需經過一次,答案繼續減小。否則,在兩個環重疊的情況下,如果我們還按照剛才的方法把第2個環與建立1條新道路的情況相結合,兩個環重疊的部分就不會被巡邏到。最終的結果是兩個環重疊的部分由只需經過一次變回了需要經過兩次。
綜上所述,我們得到了如下演算法:
1.在最初的樹上求直徑,設直徑為$l1$。然後,把直徑上的邊權取反(從1改為-1)。
2.在最長鏈邊權取反之後的樹上再次求直徑,設直徑為$l2$。
答案就是$2(n-1)-(l1-1)-(l2-1)=2n-l1-l2$。如果l2這條直徑包含l1取反的部分,就相當於兩個環重疊。減掉$(l1-1)$後,重疊的部分變成了只需經過一次,減掉$(l2-1)$後,相當於把重疊的部分加回來,變回需要經過兩次,與我們之前討論相符。時間複雜度為$o(n)$。
code:
//luogu-judger-enable-o2
#include using
namespace
std;
queue
q;const
int n=100010
;bool
v[n];
intf[n];
int n,k,e=1
,cnt,p;
int s[n<<1][2],o[n],d[n],fa[n],w[n<<1
];void add(int x,int
y)int bfs(int
xx) }
q.pop();
}return
ans;
}void mark(int
x) i=s[i][1
]; }
}void tree_dp(int
x) i=s[i][1
]; }
p=max(mx,p);f[x]=mx;
}int
main()
intss,t;
ss=bfs(1);t=bfs(ss);
bfs(1);
memset(v,
0,sizeof
(v));
if (d[ss]v[ss]=v[t]=1
;
while (d[ss]>d[t])
while (ss!=t)
if (k==1)
if (cnt==n-1)
mark(
1);tree_dp(1
); cout
<<(n-1)*2+2-cnt-p
}
洛谷 P3628 APIO2010 特別行動隊
題目描述 你有一支由 n 名預備役士兵組成的部隊,士兵從 1 到 n 編號,要將他們拆分 成若干特別行動隊調入戰場。出於默契的考慮,同一支特別行動隊中隊員的編號 應該連續,即為形如的序列。編號為 i 的士兵的初始戰鬥力為 xi 一支特別行動隊的初始戰鬥力 x 為隊內 士兵初始戰鬥力之和,即 通過長期...
洛谷 P3628 APIO2010 特別行動隊
將n個士兵分為若干組,每組連續,編號為i的士兵戰鬥力為xi 若i j士兵為一組,該組初始戰鬥力為 s sum limits xk 實際戰鬥力 a s 2 b s c a,b,c為常數 求最大實際戰鬥力 dp i max dp j a s i s j 2 b s i s j c 然後斜率優化,單調佇列...
洛谷 3628 APIO2010 特別行動隊
題目描述 你有一支由 n 名預備役士兵組成的部隊,士兵從 1 到 n 編號,要將他們拆分 成若干特別行動隊調入戰場。出於默契的考慮,同一支特別行動隊中隊員的編號 應該連續,即為形如 i,i 1,i k i,i 1,i k i,i 1 i k 的序列。編號為 i 的士兵的初始戰鬥力為 x ix i x...