日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

Luogu 4244 [SHOI2008]仙人掌图

發(fā)布時(shí)間:2023/12/20 编程问答 24 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Luogu 4244 [SHOI2008]仙人掌图 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

BZOJ 1023

如果我們把所有的環(huán)都縮成一個(gè)點(diǎn),那么整張圖就變成了一棵樹,我們可以直接$dp$算出樹的直徑。

設(shè)$f_x$表示$x$的子樹中最長鏈的長度,那么對(duì)于$x$的每一個(gè)兒子$y$,先用$f_x + f_y + 1$更新答案,再用$f_y + 1$更新$f_x$。

考慮加入環(huán)的情況,保留這個(gè)$f_x$的設(shè)定。我們可以按照搜索順序把環(huán)上第一個(gè)搜到的點(diǎn)看成環(huán)的“根”,然后用這個(gè)“根”來計(jì)算這個(gè)環(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)這個(gè)$min$不怎么好更新,可以斷環(huán)成鏈復(fù)制一倍,然后用單調(diào)隊(duì)列滑動(dòng)一個(gè)長度為$\left \lfloor \frac{m}{2} \right \rfloor$的區(qū)間即可。

在$dfs$的時(shí)候保留的$tarjan$時(shí)候采用的$dfn$和$low$數(shù)組,當(dāng)$dfn_x < low_y$的時(shí)候說明走了一條橋邊,按照原來的樹形$dp$更新答案。

處理完所有子樹之后重新掃一遍兒子,觀察是否有$fa_y \neq x$并且$dfn_y > dfn_x$的點(diǎn),如果有,那么$x$是環(huán)的“根”,$y$是環(huán)的另一個(gè)端點(diǎn)。

時(shí)間復(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]仙人掌图的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。