1008. 【SHOI2008】仙人图(II)

3 篇文章 0 订阅
1 篇文章 0 订阅

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≤50,000以及0≤m≤10,000)。其中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

Sample Output

8

Data Constraint

Hint

Solution

70%:按照求树的直径来求,将条件改成最短距离。

为什么会错呢?

 

100%:

仙人掌要特殊处理。

同样设f[ i ]表示从第 i 个点往下的最长距离。

那么在同一个环上,可以用两个点 i,j 的f[ i ]+f[ j ]+dis(i,j)来更新答案。

为了方便,我们将环转换成序列再倍长。

那么有ans=max(ans,f[ i ]+f[ j ]+i-j  )  其中i-j<=len/2,i>j

更新f[ x ]的时候就用当前点到环上任意一个点以及往下的最长距离更新即可。

Code

#include<cstdio> 
#include<cstring>
#include<algorithm>
#define I int
#define ll long long
#define z t[k]
#define F(i,a,b) for(register I i=a;i<=b;i++)
#define N 50005
using namespace std;
I n,m,k,x,y,w,nx[N<<5],ls[N],dfn[N],q[N<<1],l,r,d[N<<1],st[N],low[N],f[N],t[N<<5],ans=0,tot,tp;
char c;
void R(I &x){
	x=0;w=1;c=getchar();
	while(c<'0'||c>'9'){if(c=='-') w=-1;c=getchar();}
	while(c>='0'&&c<='9'){x=x*10+c-'0';c=getchar();}
	x*=w;
}
void add(I x,I y){
	t[++tot]=y,nx[tot]=ls[x],ls[x]=tot;
}
void work(I x){
	l=1,r=0;
	F(i,1,tot) d[i+tot]=d[i];
	F(i,1,tot<<1){
		while(l<=r&&i-q[l]>tot/2) l++;
		if(l<=r) ans=max(ans,f[d[i]]+f[d[q[l]]]+i-q[l]);
		while(l<=r&&f[d[i]]-i>=f[d[q[r]]]-q[r]) r--;
		q[++r]=i;
	}
	F(i,2,tot) f[x]=max(f[x],f[d[i]]+min(i-1,tot-i+1));
}
void tarjan(I x){
	dfn[x]=low[st[++tp]=x]=++dfn[0];
	for(I k=ls[x];k;k=nx[k]){
		if(!dfn[z]){
			tarjan(z);
			low[x]=low[z]<low[x]?low[z]:low[x];
			if(low[z]==dfn[x]){
				d[tot=1]=x;
				do{d[++tot]=w=st[tp--];}while(w!=z);
				work(x);
			}
		}
		else low[x]=dfn[z]<low[x]?dfn[z]:low[x];
	}
	ans=f[x]>ans?f[x]:ans;
}
I main(){
	freopen("cactus.in","r",stdin);
	freopen("cactus.out","w",stdout);
	R(n),R(m);
	while(m--){
		R(k);y=0;
		while(k--){
			R(x);
			if(!y){y=x;continue;}
			add(x,y),add(y,x);
			y=x;
		}
	}
	tarjan(1);
	printf("%d\n",ans);
	return 0;
}

 

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值