首页 > 其他分享 >2752. 仙人掌图

2752. 仙人掌图

时间:2022-11-22 23:01:52浏览次数:67  
标签:10 le int sum 样例 2752 仙人掌 define

题目链接

2752. 仙人掌图

如果某个无向连通图的任意一条边至多只出现在一条简单回路(simple cycle)里,我们就称这张图为仙人掌图(cactus)。

所谓简单回路就是指在图上不重复经过任何一个顶点的回路。

image

举例来说,上面的第一个例子是一张仙人图,而第二个不是——注意到它有三条简单回路:\((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\),你的任务是求出给定的仙人图的直径。

输入格式

第一行包括两个整数 \(n\) 和 \(m\)。其中 \(n\) 代表顶点个数,我们约定图中的顶点将从 \(1\) 到 \(n\) 编号。

接下来一共有 \(m\) 行。代表 \(m\) 条路径。

每行的开始有一个整数 \(k\),代表在这条路径上的顶点个数。接下来是 \(k\) 个 \(1\) 到 \(n\) 之间的整数,分别对应了一个顶点,相邻的顶点表示存在一条连接这两个顶点的边。

一条路径上可能通过一个顶点好几次,比如对于第一个样例,第一条路径从 \(3\) 经过 \(8\),又从 \(8\) 返回到了 \(3\),但是我们保证所有的边都会出现在某条路径上,而且不会重复出现在两条路径上,或者在一条路径上出现两次。

输出格式

只需输出一个数,这个数表示仙人图的直径长度。

数据范围

\(1 \le n \le 50000\),
\(0 \le m \le 10000\),
\(2 \le k \le 1000\)

输入样例1:

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

输出样例1:

8

输入样例2:

10 1
10 1 2 3 4 5 6 7 8 9 10

输出样例2:

9

样例解释

对于第一个样例:

如图,\(6\) 号点和 \(12\) 号点的最短路径长度为 \(8\),所以这张图的直径为 \(8\)。

image

解题思路

仙人掌

求解仙人掌的直径

  • 时间复杂度:\(O(n+m)\)

代码

// Problem: 仙人掌图
// Contest: AcWing
// URL: https://www.acwing.com/problem/content/description/2754/
// Memory Limit: 64 MB
// Time Limit: 1000 ms
// 
// Powered by CP Editor (https://cpeditor.org)

// %%%Skyqwq
#include <bits/stdc++.h>
 
//#define int long long
#define help {cin.tie(NULL); cout.tie(NULL);}
#define pb push_back
#define fi first
#define se second
#define mkp make_pair
using namespace std;
 
typedef long long LL;
typedef pair<int, int> PII;
typedef pair<LL, LL> PLL;
 
template <typename T> bool chkMax(T &x, T y) { return (y > x) ? x = y, 1 : 0; }
template <typename T> bool chkMin(T &x, T y) { return (y < x) ? x = y, 1 : 0; }
 
template <typename T> void inline read(T &x) {
    int f = 1; x = 0; char s = getchar();
    while (s < '0' || s > '9') { if (s == '-') f = -1; s = getchar(); }
    while (s <= '9' && s >= '0') x = x * 10 + (s ^ 48), s = getchar();
    x *= f;
}

const int N=100005,M=3*N,inf=1e8;
int n,new_n,m;
int h[2][N],e[M],ne[M],w[M],idx;
int dfn[N],low[N],timestamp;
int fu[N],fw[N],fe[N],s[N],s_tot[N];
int res,f[N],d[N],q[N];
void add(int h[],int a,int b,int c)
{
	e[idx]=b,w[idx]=c,ne[idx]=h[a],h[a]=idx++;
}
void build_circle(int x,int y,int z)
{
	int sum=z;
	for(int i=y;i!=x;i=fu[i])
	{
		s[i]=sum;
		sum+=fw[i];
	}
	s[x]=s_tot[x]=sum;
	add(h[1],x,++new_n,0);
	for(int i=y;i!=x;i=fu[i])
	{
		s_tot[i]=sum;
		add(h[1],new_n,i,min(s[i],sum-s[i]));
	}
}
void tarjan(int x,int from)
{
	dfn[x]=low[x]=++timestamp;
	for(int i=h[0][x];~i;i=ne[i])
	{
		int j=e[i];
		if(!dfn[j])
		{
			tarjan(j,i);
			fu[j]=x,fw[j]=w[i],fe[j]=i;
			low[x]=min(low[x],low[j]);
			if(dfn[x]<low[j])add(h[1],x,j,1);
		}
		else if(i!=(from^1))low[x]=min(low[x],dfn[j]);
	}
	for(int i=h[0][x];~i;i=ne[i])
	{
		int j=e[i];
		if(dfn[x]<dfn[j]&&fe[j]!=i)build_circle(x,j,w[i]);
	}
}
int dfs(int x)
{
	int d1=0,d2=0;
	for(int i=h[1][x];~i;i=ne[i])
	{
		int j=e[i];
		int t=dfs(j)+w[i];
		if(t>=d1)d2=d1,d1=t;
		else if(t>d2)d2=t;
	}
	f[x]=d1;
	if(x<=n)res=max(res,d1+d2);
	else
	{
		int sz=0;
		d[sz++]=-inf;
		for(int i=h[1][x];~i;i=ne[i])d[sz++]=f[e[i]];
		for(int i=0;i<sz;i++)d[i+sz]=d[i];
		int hh=0,tt=-1;
		for(int i=0;i<sz*2;i++)
		{
			if(hh<=tt&&i-q[hh]>sz/2)hh++;
			if(hh<=tt)res=max(res,d[i]+i+d[q[hh]]-q[hh]);
			while(hh<=tt&&d[i]-i>=d[q[tt]]-q[tt])tt--;
			q[++tt]=i;
		}
	}
	return f[x];
}
int main()
{
	memset(h,-1,sizeof h);
    scanf("%d%d",&n,&m);
    while(m--)
    {
    	int k,x,y;
    	scanf("%d%d",&k,&x);
    	while(--k)
    	{
    		scanf("%d",&y);
    		add(h[0],x,y,1);
    		add(h[0],y,x,1);
    		x=y;
    	}
    }
    new_n=n;
    tarjan(1,-1);
    dfs(1);
    printf("%d",res);
    return 0;
}

标签:10,le,int,sum,样例,2752,仙人掌,define
From: https://www.cnblogs.com/zyyun/p/16916808.html

相关文章

  • 【Coel.学习笔记】特殊的图 - 仙人掌与圆方树
    你是什么仙人?引入仙人掌是一种特殊的无向图,它的任意一条边至多只出现在一条简单回路(每个点只出现一次的回路是简单回路,特殊地,自环不算简单回路)。这里借用一下[SHOI2006......