1. 程式人生 > >1023: [SHOI2008]cactus仙人掌圖(仙人掌求直徑)

1023: [SHOI2008]cactus仙人掌圖(仙人掌求直徑)

Time Limit: 1 Sec  Memory Limit: 162 MB Submit: 3668  Solved: 1535 [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 10 1 10 1 2 3 4 5 6 7 8 9 10

Sample Output

8 9

思路:

考慮將環縮點,那麼就是普通的樹上DP求直徑

設dp[u]為只考慮以u為根的子樹,以u為一端的最長路徑

考慮對於仙人掌的每個環:

  • 暴力環中任意兩點x和y,len(x,y)+dp[x]+dp[v]最大值就是這個環對答案的貢獻(這個可以用單調佇列優化成線性)
  • 假設u是當前環最接近根的那個點,更新dp[u] = max(dp[u], dp[i]+min(deep[i]-deep[u], cnt-(deep[i]-deep[u])));

搞定(還有注意這題是求直徑不是求最長路徑)

#include<stdio.h>
#include<string.h>
#include<algorithm>
#include<map>
#include<string>
#include<math.h>
#include<queue>
#include<stack>
#include<iostream>
using namespace std;
#define LL long long
#define mod 1000000007
vector<int> G[50005];
int ans, t, Time[50005], fa[50005], low[50005], dp[50005], dep[50005], a[100005], st[100005];
void Gao(int u, int v)
{
	int cnt, i, L, R;
	cnt = 0, L = 1, R = 0;
	for(i=v;1;i=fa[i])
	{
		a[++cnt] = i;
		if(i==u)
			break;
	}
	for(i=1;i<=cnt/2;i++)
		swap(a[i], a[cnt-i+1]);
	for(i=1;i<=cnt;i++)
		a[i+cnt] = a[i];
	for(i=1;i<=2*cnt;i++)
	{
		while(R>=L && i-st[L]>cnt/2)
			L++;
		if(R>=L)
		{
			//printf("%d %d\n",  dp[a[st[L]]]+dp[a[i]], i-st[L]);
			ans = max(ans, dp[a[st[L]]]+dp[a[i]]+i-st[L]);
		}
		while(R>=L && dp[a[st[R]]]+i-st[R]<dp[a[i]])
			R--;
		st[++R] = i;
	}
	for(i=v;i!=u;i=fa[i])
		dp[u] = max(dp[u], dp[i]+min(dep[i]-dep[u], cnt-(dep[i]-dep[u])));
}
void Sech(int u, int p)
{
	int i, v;
	fa[u] = p, dep[u] = dep[p]+1;
	Time[u] = low[u] = ++t;
	for(i=0;i<G[u].size();i++)
	{
		v = G[u][i];
		if(Time[v]==0)
		{
			Sech(v, u);
			low[u] = min(low[u], low[v]);
		}
		else if(v!=p)
			low[u] = min(low[u], Time[v]);
		if(low[v]>Time[u])
		{
			ans = max(ans, dp[u]+dp[v]+1);
			dp[u] = max(dp[u], dp[v]+1);
		}
	}
	for(i=0;i<G[u].size();i++)
	{
		v = G[u][i];
		if(fa[v]!=u && Time[v]>Time[u])
			Gao(u, v);
	}
}
int main(void)
{
	int n, k, m, i, x, y;
	scanf("%d%d", &n, &m);
	for(i=1;i<=m;i++)
	{
		scanf("%d%d", &k, &x), k--;
		while(k--)
		{
			scanf("%d", &y);
			G[x].push_back(y);
			G[y].push_back(x);
			x = y;
		}
	}
	Sech(1, 0);
	printf("%d\n", ans);
	return 0;
}
/*
10 1
11 1 2 3 4 5 6 7 8 9 10 1
*/