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
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
HINT
对第一个样例的说明:如图,6号点和12号点的最短路径长度为8,所以这张图的直径为8。
【注意】使用Pascal语言的选手请注意:你的程序在处理大数据的时候可能会出现栈溢出。如果需要调整栈空间的大小,可以在程序的开头填加一句:{$M 5000000},其中5000000即指代栈空间的大小,请根据自己的程序选择适当的数值。
1 #include<cstdio> 2 #include<iostream> 3 #include<cmath> 4 #include<vector> 5 #include<cstring> 6 #include<algorithm> 7 #define maxn 100005 8 #define maxm 300005 9 using namespace std; 10 char ch; 11 bool ok; 12 void read(int &x){ 13 for (ok=0,ch=getchar();!isdigit(ch);ch=getchar()) if (ch=='-') ok=1; 14 for (x=0;isdigit(ch);x=x*10+ch-'0',ch=getchar()); 15 if (ok) x=-x; 16 } 17 int n,m,x,a,b,siz,ans; 18 struct Edge{ 19 int u,v; 20 }edge[maxm]; 21 int idx,dfn[maxn],low[maxn],top,stack[maxn],cnt; 22 unsigned int bnm[maxn]; 23 vector<int> belong[maxn],scc[maxn]; 24 int list[maxn],head,tail,que[maxn],f[maxn]; 25 struct Graph{ 26 int tot,now[maxn<<1],son[maxm],pre[maxm]; 27 void put(int a,int b){pre[++tot]=now[a],now[a]=tot,son[tot]=b;} 28 void add(int a,int b){put(a,b),put(b,a);} 29 void dfs(int u,int fa){ 30 dfn[u]=low[u]=++idx,stack[++top]=u; 31 for (int p=now[u],v=son[p];p;p=pre[p],v=son[p]) 32 if (!dfn[v]) dfs(v,u),low[u]=min(low[u],low[v]); 33 else if (v!=fa) low[u]=min(low[u],dfn[v]); 34 if (dfn[u]==low[u]){ 35 top--; 36 if (fa) bnm[u]++,bnm[fa]++,edge[++siz]=(Edge){fa,u}; 37 } 38 if (low[u]==dfn[fa]){ 39 int v; ++cnt; 40 belong[fa].push_back(cnt),scc[cnt].push_back(fa); 41 do{v=stack[top--],belong[v].push_back(cnt),scc[cnt].push_back(v);}while (v!=u); 42 } 43 } 44 void calc1(int u,int fa){ 45 int t=0,idx=u-n,len=scc[idx].size(),siz=0; 46 for (int i=0;i<len;i++) list[++siz]=scc[idx][i]; 47 for (int i=0;i<len;i++) list[++siz]=scc[idx][i]; 48 head=1,tail=0; 49 for (int i=1;i<=siz;i++){ 50 while (head<=tail&&i-que[head]>(len>>1)) head++; 51 if (head<=tail) ans=max(ans,f[list[que[head]]]+f[list[i]]+i-que[head]); 52 while (head<=tail&&f[list[que[tail]]]-que[tail]<f[list[i]]-i) tail--; 53 que[++tail]=i; 54 if (list[i]==fa&&!t) t=i-1; 55 } 56 for (int i=0;i<len;i++){ 57 int d=abs(i-t); if (d>(len>>1)) d=len-d; 58 f[u]=max(f[u],f[scc[idx][i]]+d); 59 } 60 } 61 void calc2(int u,int fa){ 62 int ans1=0,ans2=0; 63 for (int p=now[u],v=son[p];p;p=pre[p],v=son[p]) 64 if (v!=fa){ 65 ans2=max(ans2,f[v]+(v<=n)); 66 if (ans1<ans2) swap(ans1,ans2); 67 } 68 ans=max(ans,ans1+ans2),f[u]=ans1; 69 } 70 void tree_dp(int u,int fa){ 71 for (int p=now[u],v=son[p];p;p=pre[p],v=son[p]) if (v!=fa) tree_dp(v,u); 72 if (u>n) calc1(u,fa); else calc2(u,fa); 73 } 74 }G1,G2; 75 int main(){ 76 read(n),read(m); 77 for (int i=1;i<=m;i++){ 78 read(x),read(a); 79 for (x--;x;x--) read(b),G1.add(a,b),a=b; 80 } 81 for (int i=1;i<=n;i++) if (!dfn[i]) G1.dfs(i,0); 82 for (int i=1;i<=siz;i++) G2.add(edge[i].u,edge[i].v); 83 for (int i=1;i<=n;i++) if (bnm[i]+belong[i].size()>=2) 84 for (unsigned int j=0;j<belong[i].size();j++) G2.add(i,n+belong[i][j]); 85 int root=cnt?n+1:1; 86 G2.tree_dp(root,0); 87 printf("%d\n",ans); 88 return 0; 89 }