zoukankan      html  css  js  c++  java
  • 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} ight floor$的区间即可。

    在$dfs$的时候保留的$tarjan$时候采用的$dfn$和$low$数组,当$dfn_x < low_y$的时候说明走了一条桥边,按照原来的树形$dp$更新答案。

    处理完所有子树之后重新扫一遍儿子,观察是否有$fa_y eq 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
    ", ans);
        return 0;
    }
    View Code
  • 相关阅读:
    MD53D模型
    WPF 关于变换
    WPF 关于圆角的制作
    [转载]MVVM、MVVMLight、MVVMLight Toolkit之我见
    Surface Pro 调整空间大小
    WPF 图片浏览 伪3D效果
    WPF 实现视频循环播放
    WPF MediElement 视频播放
    WPF INotifyPropertyChanged
    Matlab摄像头视频基本处理
  • 原文地址:https://www.cnblogs.com/CzxingcHen/p/9901967.html
Copyright © 2011-2022 走看看