1. 程式人生 > >Luogu 4244 [SHOI2008]仙人掌圖

Luogu 4244 [SHOI2008]仙人掌圖

BZOJ 1023

如果我們把所有的環都縮成一個點,那麼整張圖就變成了一棵樹,我們可以直接$dp$算出樹的直徑。

設$f_x$表示$x$的子樹中最長鏈的長度,那麼對於$x$的每一個兒子$y$,先用$f_x + f_y + 1$更新答案,再用$f_y + 1$更新$f_x$。

考慮加入環的情況,保留這個$f_x$的設定。我們可以按照搜尋順序把環上第一個搜到的點看成環的“根”,然後用這個“根”來計算這個環。

假設有環$1, 2, 3, ..., m$,$1$是環的“根”,那麼我們可以用$f_i + f_j + min(j - i, m - (j - i))\ (i < j)$來更新答案,然後用$max(f_i + min(i - 1, m - (i - 1)))$來更新$f_1$。

發現這個$min$不怎麼好更新,可以斷環成鏈複製一倍,然後用單調佇列滑動一個長度為$\left \lfloor \frac{m}{2} \right \rfloor$的區間即可。

在$dfs$的時候保留的$tarjan$時候採用的$dfn$和$low$陣列,當$dfn_x < low_y$的時候說明走了一條橋邊,按照原來的樹形$dp$更新答案。

處理完所有子樹之後重新掃一遍兒子,觀察是否有$fa_y \neq x$並且$dfn_y > dfn_x$的點,如果有,那麼$x$是環的“根”,$y$是環的另一個端點。

時間複雜度應該是$O(n)$吧。

Code:

#include <cstdio>
#include 
<cstring> using namespace std; const int N = 5e4 + 5; const int M = 2e5 + 5; int n, m, tot = 0, head[N], f[N], g[N << 1], q[N << 1]; int ans = 0, dfsc = 0, fa[N], dfn[N], low[N], dep[N]; struct Edge { int to, nxt; } e[M]; inline void add(int from, int to) { e[
++tot].to = to; e[tot].nxt = head[from]; head[from] = tot; } inline void read(int &X) { X = 0; char ch = 0; int op = 1; for(; ch > '9' || ch < '0'; ch = getchar()) if(ch == '-') op = -1; for(; ch >= '0' && ch <= '9'; ch = getchar()) X = (X << 3) + (X << 1) + ch - 48; X *= op; } inline void chkMax(int &x, int y) { if(y > x) x = y; } inline int min(int x, int y) { return x > y ? y : x; } inline void swap(int &x, int &y) { int t = x; x = y; y = t; } inline void solve(int x, int y) { int cnt = 0; for(int tmp = y; tmp != x; tmp = fa[tmp]) g[++cnt] = f[tmp]; g[++cnt] = f[x]; for(int i = 1; i <= cnt / 2; i++) swap(g[i], g[cnt - i + 1]); /* int cnt = dep[y] - dep[x] + 1; for(int tmp = y; tmp != x; tmp = fa[tmp]) g[cnt--] = f[tmp]; g[cnt] = f[x]; cnt = dep[y] - dep[x] + 1; */ for(int i = 1; i < cnt; i++) g[i + cnt] = g[i]; int l = 1, r = 0; for(int i = 1; i < 2 * cnt; i++) { for(; l <= r && i - q[l] > (cnt / 2); ++l); if(l <= r) chkMax(ans, g[i] + g[q[l]] + i - q[l]); for(; l <= r && g[q[r]] - q[r] <= g[i] - i; --r); q[++r] = i; } for(int i = 2; i <= cnt; i++) chkMax(f[x], g[i] + min(i - 1, cnt - (i - 1))); } void dfs(int x, int fat, int depth) { fa[x] = fat, dep[x] = depth; low[x] = dfn[x] = ++dfsc; for(int i = head[x]; i; i = e[i].nxt) { int y = e[i].to; if(y == fat) continue; if(!dfn[y]) { dfs(y, x, depth + 1); low[x] = min(low[x], low[y]); } else low[x] = min(low[x], dfn[y]); if(low[y] > dfn[x]) { chkMax(ans, f[x] + f[y] + 1); chkMax(f[x], f[y] + 1); } } for(int i = head[x]; i; i = e[i].nxt) { int y = e[i].to; // if(y == fat) continue; if(fa[y] != x && dfn[y] > dfn[x]) solve(x, y); } } int main() { read(n), read(m); for(int k, i = 1; i <= m; i++) { read(k); for(int lst, now, j = 1; j <= k; j++) { read(now); if(j != 1) add(now, lst), add(lst, now); lst = now; } } dfs(1, 0, 1); printf("%d\n", ans); return 0; }
View Code