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](其实之前算环上两条链的时候有一种情况算不到,现在就是在做这个哦,这句如果不理解就忽视掉吧,不是很重要……)

【BZOJ】【1023】【SHOI2008】cactus仙人掌图_BZOJ【BZOJ】【1023】【SHOI2008】cactus仙人掌图_DP优化_02
 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\n",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)。所谓简单回路就是指在图上不重复经过任何一个顶点的回路。

 【BZOJ】【1023】【SHOI2008】cactus仙人掌图_单调队列优化_03

举例来说,上面的第一个例子是一张仙人图,而第二个不是——注意到它有三条简单回路:(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。


 【BZOJ】【1023】【SHOI2008】cactus仙人掌图_DP_04


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

Source

[Submit][Status][Discuss]