zoukankan      html  css  js  c++  java
  • 【BZOJ】【1023】【SHOI2008】cactus仙人掌图

    DP+单调队列/仙人掌


      题解:http://hzwer.com/4645.html->http://z55250825.blog.163.com/blog/static/150230809201412793151890/

      QAQ了

      呃……第一次做仙人掌的题目……感觉性质还是蛮神奇的(我是不是应该先做一点环套树的题目呢?>_>)

      每个点都只会在一个简单环上,所以在dfs的时候,对于一个环,它上面的点是深度连续的一段(沿着father可以遍历这个环!),然后最后一个点再指回起始点,所以只要low改变了,就找到了一个环。。。

      判是否在环上也很好判,看下low就可以了

      简单了解了仙人掌的情况,我们来看下这道题怎么做:

      如果是一棵树的话,我们只要Tree DP就可以了……令f[i]表示以 i 为根的最长的链,子树合并的时候随便搞搞……比较简单。

      现在麻烦的是出现了环= =所以f[i]的定义本身就有些纠结……

      既然这样,那么我们就把问题分成两部分来做吧!树的部分和环的部分!

      对于树的部分……同上

      那么环的部分是什么情况呢?我们可以令f[i]表示环上这个点向外的最长链的长度(这个定义对于树的部分不冲突,所以不用分开算)然后环上的最长链就是:$ans=max{ f[i]+f[j]+min{j-i,tot-j+i} }$呃……其实意思就是找环上两条链出来,然后加上它俩直接的距离求个最大值。最后需要更新一下环的“根”节点的f[root],这时它的子树已经完全计算完毕了,那么f[root]就应该表示以它为根的最长链,那么我们就可以用环上其他点连出去的链来更新这个f[root](其实之前算环上两条链的时候有一种情况算不到,现在就是在做这个哦,这句如果不理解就忽视掉吧,不是很重要……)

     1 /**************************************************************
     2     Problem: 1023
     3     User: Tunix
     4     Language: C++
     5     Result: Accepted
     6     Time:212 ms
     7     Memory:9792 kb
     8 ****************************************************************/
     9  
    10 //BZOJ 1023
    11 #include<cstdio>
    12 #include<cstring>
    13 #include<cstdlib>
    14 #include<iostream>
    15 #include<algorithm>
    16 #define rep(i,n) for(int i=0;i<n;++i)
    17 #define F(i,j,n) for(int i=j;i<=n;++i)
    18 #define D(i,j,n) for(int i=j;i>=n;--i)
    19 using namespace std;
    20 typedef long long LL;
    21 inline int getint(){
    22     int v=0,r=1; char ch=getchar();
    23     for(;!isdigit(ch);ch=getchar()) if(ch=='-')r=-1;
    24     for(; isdigit(ch);ch=getchar()) v=v*10+ch-'0';
    25     return v*r;
    26 }
    27 const int N=1e5+10,INF=~0u>>2;
    28 /*********************template******************/
    29 int to[N<<2],next[N<<2],head[N],cnt;
    30 void add(int x,int y){
    31     to[++cnt]=y; next[cnt]=head[x]; head[x]=cnt;
    32     to[++cnt]=x; next[cnt]=head[y]; head[y]=cnt;
    33 }
    34 int dfn[N],low[N],fa[N],dep[N],dfs_clock,a[N],Q[N],f[N];
    35 int n,m,ans;
    36 void dp(int root,int x){
    37     int tot=dep[x]-dep[root]+1,j=tot;
    38     for(int i=x;i!=root;i=fa[i])
    39         a[j--]=f[i];
    40     a[j]=f[root];
    41     F(i,1,tot) a[i+tot]=a[i];
    42     int l=0,r=-1;
    43     Q[++r]=1;
    44     F(i,2,tot*2){
    45         while(l<=r && i-Q[l]>tot/2) l++;
    46         ans=max(ans,a[i]+i+a[Q[l]]-Q[l]);
    47         while(l<=r && a[Q[r]]-Q[r]<a[i]-i) r--;
    48         Q[++r]=i;
    49     }
    50     F(i,2,tot)
    51         f[root]=max(f[root],a[i]+min(i-1,tot-i+1));
    52 }
    53 void dfs(int x){
    54     dfn[x]=low[x]=++dfs_clock;
    55     for(int i=head[x];i;i=next[i])
    56         if (to[i]!=fa[x]){
    57             if (!dfn[to[i]]){
    58                 fa[to[i]]=x;
    59                 dep[to[i]]=dep[x]+1;
    60                 dfs(to[i]);
    61                 low[x]=min(low[x],low[to[i]]);
    62             }else low[x]=min(low[x],dfn[to[i]]);
    63             if (dfn[x]<low[to[i]]){
    64                 ans=max(ans,f[x]+f[to[i]]+1);
    65                 f[x]=max(f[x],f[to[i]]+1);
    66             }
    67         }
    68     for(int i=head[x];i;i=next[i])
    69         if (fa[to[i]]!=x && dfn[x]<dfn[to[i]])
    70             dp(x,to[i]);
    71 }
    72 int main(){
    73 #ifndef ONLINE_JUDGE
    74     freopen("1023.in","r",stdin);
    75     freopen("std.out","w",stdout);
    76 #endif
    77     n=getint(); m=getint();
    78     F(i,1,m){
    79         int k=getint(),a=getint(),b;
    80         F(i,2,k){
    81             b=getint();
    82             add(a,b);
    83             a=b;
    84         }
    85     }
    86     dfs(1);
    87     printf("%d
    ",ans);
    88     return 0;
    89 }
    View Code

    1023: [SHOI2008]cactus仙人掌图

    Time Limit: 1 Sec  Memory Limit: 162 MB
    Submit: 1329  Solved: 532
    [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

    9

    HINT

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


     


    【注意】使用Pascal语言的选手请注意:你的程序在处理大数据的时候可能会出现栈溢出。如果需要调整栈空间的大小,可以在程序的开头填加一句:{$M 5000000},其中5000000即指代栈空间的大小,请根据自己的程序选择适当的数值。

    Source

    [Submit][Status][Discuss]
  • 相关阅读:
    ARTS第十一周
    ARTS第十周
    ARTS第九周
    一.Java技术现象
    ARTS第八周
    2019书单
    10.枚举的使用
    9.文件输入与输出
    软件模块化设计
    8.String API
  • 原文地址:https://www.cnblogs.com/Tunix/p/4421596.html
Copyright © 2011-2022 走看看