【BZOJ 1023】[SHOI2008]cactus仙人掌图

【题目链接】:http://www.lydsy.com/JudgeOnline/problem.php?id=1023

【题意】

【题解】

如果不考虑有环的情况;
那么有一个经典的求树的直径的方法;
首先;
树的直径的两端的端点必然都在树的叶子上(或在根节点,考虑一条链的情况);

设f[i][0]表示离i这个点最远的叶子节点的距离
f[i][1]表示离i这个点第二远的叶子节点的距离
更新的话
f[x][0]=max(f[son][0]+1);
f[x][1] = max(second(f[son][0])+1);
则可以通过dp求出来所有的节点的f值,取max{f[i][0]+f[i][1]}就是它的直径了;
这里我们可以降成一维的即
ans = max(ans,f[x]+f[son]+1),f[x]=max(f[son]+1);
这里f[x]=max(f[son]+1)在ans更新完后才更新;
这个做法就等同于上面那个做法;
然后该题的情况就是多了一个环;
环的话只要通过环上的非最高点对(x,y);
用f(x)+f(y)+dist(x,y)来更新答案ans;
设环上的最高点为u;
然后用非最高点x的f[x]+dist(x,u)来更新f[x]即可;
挺自然的一个做法吧;
这里的
f(x)+f(y)+dist(x,y)
可以写成
f[x]+f[y]+x-y
这里的x和y是把环破成直线之后x和y在直线上的下标;
x>y
则我们只要维护f[y]-y不下降就可以了
用单调队列搞;
然后因为是最短距离;
所以f[y]-y必须要满足x-y< n/2
这里的n是这个环的长度;
也因为是最短距离;
所以你要把直线的长度延伸到2*n,不然可能会漏解.

【完整代码】

#include <bits/stdc++.h>
using namespace std;
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
#define LL long long
#define rep1(i,a,b) for (int i = a;i <= b;i++)
#define rep2(i,a,b) for (int i = a;i >= b;i--)
#define mp make_pair
#define pb push_back
#define fi first
#define se second
#define rei(x) scanf("%d",&x)
#define rel(x) scanf("%lld",&x)
#define ref(x) scanf("%lf",&x)

typedef pair<int, int> pii;
typedef pair<LL, LL> pll;

const int dx[9] = { 0,1,-1,0,0,-1,-1,1,1 };
const int dy[9] = { 0,0,0,-1,1,-1,1,-1,1 };
const double pi = acos(-1.0);
const int N = 5e4+100;

int n, m,k,a[N*2],dfn[N],low[N],num,father[N],f[N],dep[N],ans,q[2*N];
vector <int> g[N];

void input_data()
{
    rei(n), rei(m);
    rep1(i, 1, m)
    {
        rei(k);
        rep1(j, 1, k)
            rei(a[j]);
        rep1(j, 1, k - 1)
            g[a[j]].push_back(a[j + 1]), g[a[j + 1]].push_back(a[j]);
    }
}

void dp(int root, int x)
{
    int n = dep[x] - dep[root] + 1;
    for (int i = x; i != root; i = father[i])
        a[n--] = f[i];
    a[n] = f[root], n = dep[x] - dep[root] + 1;
    rep1(i, n + 1, 2 * n)
        a[i] = a[i - n];
    int h = 1, t = 1;
    q[h] = 1;
    rep1(i, 2, n + n / 2)
    {
        while (h <= t && i - q[h] > n / 2) h++;
        ans = max(ans, a[i] + a[q[h]] + i - q[h]);
        while (h <= t && a[q[h]] - q[h] <= a[i] - i) t--;
        q[++t] = i;
    }
    rep1(i, 2, n)
        f[root] = max(f[root], a[i] + min(i - 1, n - i + 1));
}

void Tarjan(int x)
{
    dfn[x] = low[x] = ++num;
    int len = g[x].size();
    rep1(i, 0, len - 1)
    {
        int y = g[x][i];
        if (y == father[x]) continue;
        if (dfn[y] == 0) dep[y] = dep[x] + 1, father[y] = x, Tarjan(y);
        low[x] = min(low[x], low[y]);
        if (dfn[x] < low[y])
            ans = max(ans, f[x] + f[y] + 1), f[x] = max(f[x], f[y] + 1);
    }
    rep1(i, 0, len - 1)
    {
        int y = g[x][i];
        if (y!=father[x] && father[y] != x && dfn[x] < dfn[y])
            dp(x, y);
    }
}

int main()
{
    //freopen("F:\\rush.txt", "r", stdin);
    input_data();
    Tarjan(1);
    printf("%d\n", ans);
    //printf("\n%.2lf sec \n", (double)clock() / CLOCKS_PER_SEC);
    return 0;
}
posted @ 2017-10-04 18:45  AWCXV  阅读(155)  评论(0编辑  收藏  举报