Luogu 4244 [SHOI2008]仙人掌图
BZOJ 1023
如果我們把所有的環(huán)都縮成一個點,那么整張圖就變成了一棵樹,我們可以直接$dp$算出樹的直徑。
設(shè)$f_x$表示$x$的子樹中最長鏈的長度,那么對于$x$的每一個兒子$y$,先用$f_x + f_y + 1$更新答案,再用$f_y + 1$更新$f_x$。
考慮加入環(huán)的情況,保留這個$f_x$的設(shè)定。我們可以按照搜索順序把環(huán)上第一個搜到的點看成環(huán)的“根”,然后用這個“根”來計算這個環(huán)。
假設(shè)有環(huán)$1, 2, 3, ..., m$,$1$是環(huán)的“根”,那么我們可以用$f_i + f_j + min(j - i, m - (j - i))\ (i < j)$來更新答案,然后用$max(f_i + min(i - 1, m - (i - 1)))$來更新$f_1$。
發(fā)現(xiàn)這個$min$不怎么好更新,可以斷環(huán)成鏈復(fù)制一倍,然后用單調(diào)隊列滑動一個長度為$\left \lfloor \frac{m}{2} \right \rfloor$的區(qū)間即可。
在$dfs$的時候保留的$tarjan$時候采用的$dfn$和$low$數(shù)組,當(dāng)$dfn_x < low_y$的時候說明走了一條橋邊,按照原來的樹形$dp$更新答案。
處理完所有子樹之后重新掃一遍兒子,觀察是否有$fa_y \neq x$并且$dfn_y > dfn_x$的點,如果有,那么$x$是環(huán)的“根”,$y$是環(huán)的另一個端點。
時間復(fù)雜度應(yīng)該是$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?
轉(zhuǎn)載于:https://www.cnblogs.com/CzxingcHen/p/9901967.html
總結(jié)
以上是生活随笔為你收集整理的Luogu 4244 [SHOI2008]仙人掌图的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: oppok3如何刷机_OPPO K3 P
- 下一篇: 【酱菜物联】微信小程序实现远程控制LED