APIO2010 巡邏
阿新 • • 發佈:2018-07-29
ret pan clas hive www. include get str arc 化簡得 \(2n-l_1-l_2\) 。
為獲得更好的閱讀體驗請訪問我的 Blog
Luogu
每添加一條邊就會形成一個環,環上的邊就都只用經過一次。所以 \(k=1\) 時候求下樹的直徑,令直徑為 \(l_1\), \(2*(n-1)-l_1+1\) 就是答案了。
接下來我們來考慮 \(k=2\) 的時候。
肯定也是要連一條跟直徑類似的路徑的,但是可能會有邊與之前 \(k=1\) 時連的重復。這時候本來這條邊只需經過一次,現在變成了兩次。所以,要將 \(k=1\) 時求出的直徑上的路徑的邊權都變成 \(-1\) ,然後再求一遍直徑。令這條直徑為 \(l_2\) 答案就為 \(2*(n-1) - l_1 + 1 - l_2 + 1\)
這裏使用 樹形Dp 來求樹的直徑。
#include <iostream> #include <cstdio> #include <cstring> const int MaxN = 1e5 + 5; int N, K, L1, L2, Edge, Ans, T1, T2; int Dp[MaxN], FST[MaxN], W[MaxN], F[MaxN], Vis[MaxN]; struct Linker { int to, nxt; Linker(){} Linker(int x, int y) { to = y; nxt = FST[x]; } } E[MaxN << 1]; inline int read() { register int x = 0; register char ch = getchar(); while(!isdigit(ch)) ch = getchar(); while(isdigit(ch)) { x = x * 10 + ch - ‘0‘; ch = getchar(); } return x; } inline void AddEdge(int u, int v) { E[++Edge] = Linker(u, v); FST[u] = Edge; } void DP(int x) { Vis[x] = 1; for(int k = FST[x]; k; k = E[k].nxt) { int to = E[k].to, w = W[to] == 0 ? 1 : - 1; if(Vis[to]) continue; DP(to); if(Dp[x] + Dp[to] + w > Ans) { Ans = Dp[x] + Dp[to] + w; T1 = F[x]; T2 = to; } if(Dp[to] + w > Dp[x]) { Dp[x] = Dp[to] + w; F[x] = to; } } } void paint(int x) { if(!x) return; W[x] = 1; paint(F[x]); } int main() { N = read(); K = read(); for(int i = 1; i < N; ++i) { int u = read(), v = read(); AddEdge(u, v); AddEdge(v, u); } DP(1); L1 = Ans; if(K == 1) { printf("%d\n", 2 * (N - 1) - L1 + 1); return 0; } else { paint(T1); paint(T2); Ans = 0; memset(Dp, 0, sizeof(Dp)); memset(F, 0, sizeof(F)); memset(Vis, 0, sizeof(Vis)); DP(1); L2 = Ans; printf("%d\n", 2 * N - L1 - L2); } return 0; }
APIO2010 巡邏