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
*/