• bzoj1023 [SHOI2008]cactus仙人掌图


    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即指代栈空间的大小,请根据自己的程序选择适当的数值。

    正解:仙人掌+$tarjan$+单调队列。

    这题是仙人掌入门题,然而我看了好久的题解才看懂。。

    首先要明确一点,树的直径算法,也就是两遍$bfs$的做法是错的,这个在$2015$年集训队论文集中$wys$的论文里有指出。

    然后这是棵仙人掌,我们考虑一下如何求解。

    首先我们还是和树形$dp$的做法一样。先遍历一遍整棵仙人掌,搞清楚仙人掌的结构是怎样的。

    当我们遍历的结点不在环内时,直接按照树形$dp$的做法,用最长链和次长链更新$ans$就行了。

    当我们遍历的结点在环内时,我们直接把环抠出来,另外处理。

    然后我们可以把环拉成一条链,也就是把环倍长。

    我们用这个环更新答案时,要考虑一种情况,就是环上两个点的最短距离和两个点子树内的最长链,也就是$f[i]$相加。

    这个情况很不好处理,但是我们可以发现,把环拉成序列以后,我们要求的就是在序列中距离在序列长度的$frac{1}{4}$以内(因为环已经倍长辣)的最优解。

    我们把环拉成序列以后,因为扫描序列是顺时针操作的,所以序列中相邻的点在环中也是相邻的,所以两点之间的距离就是它们在序列中的下标差。

    于是我们的$ans$就是$max(f[u]+f[v]+u-v)$($idu>idv$且$idu-idv<frac{1}{4}len$,$len$为序列长度)。于是我们发现这个东西可以用单调队列做。

    我们更新完答案以后,再顺便更新一下环顶的$f$值,因为其他的$f$值以后都不会再用到了,我们只要更新环顶的$f$就能做到不影响答案了。

    于是这道题就被我们完美地解决了。

     1 //It is made by wfj_2048~
     2 #include <algorithm>
     3 #include <iostream>
     4 #include <complex>
     5 #include <cstring>
     6 #include <cstdlib>
     7 #include <cstdio>
     8 #include <vector>
     9 #include <cmath>
    10 #include <queue>
    11 #include <stack>
    12 #include <map>
    13 #include <set>
    14 #define inf (1<<30)
    15 #define N (200010)
    16 #define il inline
    17 #define RG register
    18 #define ll long long
    19 #define File(s) freopen(s".in","r",stdin),freopen(s".out","w",stdout)
    20 
    21 using namespace std;
    22 
    23 struct edge{ int nt,to; }g[1000010];
    24 
    25 int head[N],fa[N],dep[N],dfn[N],low[N],f[N],a[N],q[N],n,m,num,cnt,ans;
    26 
    27 il int gi(){
    28     RG int x=0,q=1; RG char ch=getchar();
    29     while ((ch<'0' || ch>'9') && ch!='-') ch=getchar();
    30     if (ch=='-') q=-1,ch=getchar();
    31     while (ch>='0' && ch<='9') x=x*10+ch-48,ch=getchar();
    32     return q*x;
    33 }
    34 
    35 il void insert(RG int from,RG int to){
    36     g[++num]=(edge){head[from],to},head[from]=num; return;
    37 }
    38 
    39 il void circle(RG int rt,RG int x){ //环上dp
    40     RG int top=dep[x]-dep[rt]+1; a[1]=f[rt];
    41     for (RG int i=x;i!=rt;i=fa[i]) a[top--]=f[i];
    42     RG int h=1,t=1; top=dep[x]-dep[rt]+1,q[1]=1;
    43     for (RG int i=1;i<=top;++i) a[i+top]=a[i]; //环倍长
    44     for (RG int i=2;i<=(top<<1);++i){
    45     while (h<t && i-q[h]>(top>>1)) h++;
    46     ans=max(ans,a[i]+a[q[h]]+i-q[h]);
    47     while (h<=t && a[q[t]]-q[t]<=a[i]-i) t--;
    48     q[++t]=i;
    49     }
    50     for (RG int i=2;i<=top;++i) //更新环顶结点的f值
    51     f[rt]=max(f[rt],a[i]+min(i-1,top-i+1));
    52     return;
    53 }
    54 
    55 il void dfs(RG int x,RG int p){
    56     fa[x]=p,dep[x]=dep[p]+1;
    57     dfn[x]=low[x]=++cnt; RG int v;
    58     for (RG int i=head[x];i;i=g[i].nt){
    59     v=g[i].to; if (v==p) continue;
    60     if (!dfn[v]) dfs(v,x),low[x]=min(low[x],low[v]);
    61     else low[x]=min(low[x],dfn[v]);
    62     if (dfn[x]<low[v]){ //没有形成环的情况
    63         ans=max(ans,f[x]+f[v]+1);
    64         f[x]=max(f[x],f[v]+1);
    65     }
    66     }
    67     for (RG int i=head[x];i;i=g[i].nt){ //判断是否有环
    68     v=g[i].to; if (v==p) continue;
    69     if (fa[v]!=x && dfn[x]<dfn[v]) circle(x,v);
    70     }
    71     return;
    72 }
    73 
    74 il void work(){
    75     n=gi(),m=gi();
    76     for (RG int i=1,k,x,v;i<=m;++i){
    77     k=gi(),x=gi();
    78     for (RG int j=1;j<k;++j)
    79         v=gi(),insert(x,v),insert(v,x),x=v;
    80     }
    81     dfs(1,0); printf("%d
    ",ans); return;
    82 }
    83 
    84 int main(){
    85     File("cactus");
    86     work();
    87     return 0;
    88 }
  • 相关阅读:
    边框上带有文字
    满屏展示
    javaScript中SetInterval与setTimeout区别
    圆形进度条
    抢购(秒杀)业务的技术要点
    Sybase 动态改变存储过程里查询的数据库
    在Hibernate映射文件里配置Sequence
    初学struts2杂乱笔记
    Struts2的学习链接
    DWR框架在web.xml的完整配置及注释
  • 原文地址:https://www.cnblogs.com/wfj2048/p/6641142.html
Copyright © 2020-2023  润新知