• bzoj1023 [SHOI2008]cactus仙人掌图


    1023: [SHOI2008]cactus仙人掌图

    Time Limit: 1 Sec  Memory Limit: 162 MB
    Submit: 3174  Solved: 1312
    [Submit][Status][Discuss]

    Description

      如果某个无向连通图的任意一条边至多只出现在一条简单回路(simple cycle)里,我们就称这张图为仙人掌
    图(cactus)。所谓简单回路就是指在图上不重复经过任何一个顶点的回路。

     

      举例来说,上面的第一个例子是一张仙人图,而第二个不是——注意到它有三条简单回路:(4,3,2,1,6
    ,5,4)、(7,8,9,10,2,3,7)以及(4,3,7,8,9,10,2,1,6,5,4),而(2,3)同时出现在前两
    个的简单回路里。另外,第三张图也不是仙人图,因为它并不是连通图。显然,仙人图上的每条边,或者是这张仙
    人图的桥(bridge),或者在且仅在一个简单回路里,两者必居其一。定义在图上两点之间的距离为这两点之间最
    短路径的距离。定义一个图的直径为这张图相距最远的两个点的距离。现在我们假定仙人图的每条边的权值都是1
    ,你的任务是求出给定的仙人图的直径。

    Input

      输入的第一行包括两个整数n和m(1≤n≤50000以及0≤m≤10000)。其中n代表顶点个数,我们约定图中的顶
    点将从1到n编号。接下来一共有m行。代表m条路径。每行的开始有一个整数k(2≤k≤1000),代表在这条路径上
    的顶点个数。接下来是k个1到n之间的整数,分别对应了一个顶点,相邻的顶点表示存在一条连接这两个顶点的边
    。一条路径上可能通过一个顶点好几次,比如对于第一个样例,第一条路径从3经过8,又从8返回到了3,但是我们
    保证所有的边都会出现在某条路径上,而且不会重复出现在两条路径上,或者在一条路径上出现两次。

    Output

      只需输出一个数,这个数表示仙人图的直径长度。

    Sample Input

    15 3
    9 1 2 3 4 5 6 7 8 3
    7 2 9 10 11 12 13 10
    5 2 14 9 15 10 8
    10 1
    10 1 2 3 4 5 6 7 8 9 10

    Sample Output

    8
    9

    HINT

    对第一个样例的说明:如图,6号点和12号点的最短路径长度为8,所以这张图的直径为8。


     


    【注意】使用Pascal语言的选手请注意:你的程序在处理大数据的时候可能会出现栈溢出。

    如果需要调整栈空间的大小,可以在程序的开头填加一句:{$M 5000000},其中5000000即

    指代栈空间的大小,请根据自己的程序选择适当的数值。

    分析:比较难的一道题.

              考虑dp,设f[i]表示以i为根的子树中,i为端点的最长链的长度.dfs,用经过点u的最长链+次长链更新答案.并用当前链的长度更新最长链的长度.如果遇到环了,就要单独处理.在环上的答案肯定是环上两个点延伸出去的两条链+在环上经过的最短路.即f[x] + f[y] + pos[x] - pos[y].将环变成一段区间,那么转移中y的下标就有了限制,又因为是求最值,所以可以用单调队列优化.

              单调队列中维护的东西比较特殊,不仅有f[y],还涉及到pos[y],将f[y]-pos[y]看作一个整体来维护.因为距离的定义是最短路,所以在环上经过的路径一定是较短的那条路径.当i与队首的下标差>len/2时,说明已经不是最短路了,就要将队首提前.最后还要更新一下f[x].

              环上求最值可以用单调队列维护要记清楚了.当前点是否在树内或环内的判断标准也要熟记.

    #include <cstdio>
    #include <cstring>
    #include <iostream>
    #include <algorithm>
    
    using namespace std;
    
    const int maxn = 400010;
    
    int n,m,head[maxn],to[maxn],nextt[maxn],tot = 1;
    int q[maxn],pos[maxn],cnt,a[maxn];
    int dfs_clock,pre[maxn],low[maxn],fa[maxn],f[maxn],dist[maxn],ans;
    
    void add(int x,int y)
    {
        to[tot] = y;
        nextt[tot] = head[x];
        head[x] = tot++;
    }
    
    void solve(int x,int y)
    {
        cnt = dist[y] - dist[x] + 1;
        for (int i = y; i != x; i = fa[i])
            a[cnt--] = f[i];
        a[cnt] = f[x];
        cnt = dist[y] - dist[x] + 1;
        for (int i = 1; i <= cnt; i++)
            a[i + cnt] = a[i];
        int l = 1,r = 1;
        pos[1] = 1;
        for (int i = 2; i <= cnt * 2; i++)
        {
            while (l <= r && i - pos[l] > cnt / 2)
                l++;
            ans = max(ans,a[pos[l]] + a[i] + i - pos[l]);
            while (l <= r && a[pos[r]] - pos[r] <= a[i] - i)
                r--;
            pos[++r] = i;
        }
        for (int i = 2; i <= cnt; i++)
            f[x] = max(f[x],a[i] + min(i - 1,cnt - i + 1));
    }
    
    void dfs(int u,int faa)
    {
        pre[u] = low[u] = ++dfs_clock;
        for (int i = head[u];i;i = nextt[i])
        {
            int v = to[i];
            if (v == faa)
                continue;
            if (!pre[v])
            {
                fa[v] = u;
                dist[v] = dist[u] + 1;
                dfs(v,u);
                low[u] = min(low[u],low[v]);
            }
            else
                low[u] = min(low[u],pre[v]);
            if (pre[u] < low[v])
            {
                ans = max(ans,f[u] + f[v] + 1);
                f[u] = max(f[u],f[v] + 1);
            }
        }
        for (int i = head[u];i;i = nextt[i])
        {
            int v = to[i];
            if (v == faa)
                continue;
            if (fa[v] != u && pre[u] < pre[v]) //此时u是dfs最先进入环中的点 v在u访问其它儿子的时候就被访问到了,所以v是深度最大的点
                solve(u,v);
        }
    }
    
    int main()
    {
        scanf("%d%d",&n,&m);
        for (int i = 1; i <= m; i++)
        {
            int num,last;
            scanf("%d",&num);
            scanf("%d",&last);
            for (int i = 1; i < num; i++)
            {
                int t;
                scanf("%d",&t);
                add(last,t);
                add(t,last);
                last = t;
            }
        }
        dfs(1,0);
        printf("%d
    ",ans);
    
        return 0;
    }
  • 相关阅读:
    oracle聚合函数及行专列,pivot rollup cube
    oracle expdp导入时 提示“ORA-39002: 操作无效 ORA-39070: 无法打开日志文件 ”
    PL/SQL 美化器不能解析文本
    PL/SQL TOAD 不安装Oracle客户端连接数据库的方法
    oracle 某一字段取反
    jqgrid 加按钮列
    扩展方法 DataTable的ToList<T>
    jquery ajax调用WCF,采用System.ServiceModel.WebHttpBinding
    jquery ajax调用WCF,采用System.ServiceModel.WSHttpBinding协议
    学习WCF笔记之二
  • 原文地址:https://www.cnblogs.com/zbtrs/p/8298710.html
Copyright © 2020-2023  润新知