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

 

posted @ 2018-11-03 20:25  CzxingcHen  阅读(129)  评论(0编辑  收藏  举报