[SHOI2008]仙人掌图

[SHOI2008]仙人掌图

LG传送门

还不会仙人掌的同学可以看看我对仙人掌知识的一些梳理。

题意就是求仙人掌的直径,直径定义为图中最短路径最长的两点间的最短路径长度。

按照套路,先考虑求树的直径我们是怎么做的。设\(f[i]\)表示\(i\)往下最长链的长度,\(j\)\(i\)的儿子,转移和更新答案就是(我习惯用\(o\)表示答案):

\[f[i] = max\{f[j]\} + 1 \qquad o = max\{f[i] + f[j] + 1\}\]

考虑放到仙人掌上,对于树边直接转移和更新答案;对于非树边,先把环拎出来,可以直接转移,设\(i\)是环顶端的点,\(j\)是环上的其他点,\(k\)是环的大小:

\[f[i] = max\{f[j] + dis(i, j)\}\]

\(dis(i, j)\)可以用深度差和环大小减深度差的较小值来表示。

更新答案似乎不能直接搞,考虑断环为链,维护个单调队列:如果队首元素超过环大小的一半就出队,用准备进队的元素和队首元素更新答案,如果当前进队元素的\(f\)减去队尾元素的\(f\)大于等于他们在环上的距离就弹出队尾懒得放数学公式了,可以结合代码理解

#include 
#include 
#include 
#include 
#define R register
#define I inline
#define B 1000000
using namespace std;
const int N = 50003, M = 100003;
char buf[B], *p1, *p2;
I char gc() { return p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, B, stdin), p1 == p2) ? EOF : *p1++; }
I int rd() {
    R int f = 0;
    R char c = gc();
    while (c < 48 || c > 57)
        c = gc();
    while (c > 47 && c < 58)
        f = f * 10 + (c ^ 48), c = gc();
    return f;
}
int s[N], p[N], d[N], l[N], e[M], q[M], f[N], t, o;
vector  g[N];
I int min(int x, int y) { return x < y ? x : y; }
I int max(int x, int y) { return x > y ? x : y; }
void dfs(int x, int h) {
    p[x] = h, d[x] = l[x] = ++t;
    R int i, y;
    for (i = 0; i < s[x]; ++i) {
        if (!d[y = g[x][i]])
            dfs(y, x), l[x] = min(l[x], l[y]);
        else
            if (y ^ h)
                l[x] = min(l[x], d[y]);
        if (l[y] > d[x])
            o = max(o, f[x] + f[y] + 1), f[x] = max(f[x], f[y] + 1);
    }
    for (i = 0; i < s[x]; ++i)
        if (p[y = g[x][i]] ^ x && d[x] < d[y]) {
            R int j, k = 0, u = 1, v = 0;
            for (j = y; j ^ x; j = p[j])
                e[++k] = f[j];
            e[++k] = f[x], reverse(e + 1, e + k + 1);
            for (j = 1; j <= k; ++j)
                e[j + k] = e[j];
            k <<= 1, q[++v] = 1;
            for (j = 2; j <= k; q[++v] = j, ++j) {
                while (j - q[u] > (k >> 2) && u <= v)
                    ++u;
                if (u <= v)
                    o = max(o, e[j] + e[q[u]] + j - q[u]);
                while (e[j] - e[q[v]] >= j - q[v] && u <= v)
                    --v;
            }
            k >>= 1;
            for (j = 2; j <= k; ++j)
                f[x] = max(f[x], e[j] + min(j - 1, k - j + 1));
        }
}
int main() {
    R int n = rd(), m = rd(), i, j, k, x, y;
    for (i = 1; i <= m; ++i) {
        k = rd(), x = rd();
        for (j = 1; j < k; x = y, ++j)
            y = rd(), g[x].push_back(y), g[y].push_back(x);
    }
    for (i = 1; i <= n; ++i)
        s[i] = g[i].size();
    dfs(1, 0), printf("%d", o);
    return 0;
}

转载于:https://www.cnblogs.com/cj-chd/p/10285813.html

你可能感兴趣的:([SHOI2008]仙人掌图)