BZOJ1023: [SHOI2008]cactus仙人掌圖(仙人掌dp)

Time Limit: 1 Sec  Memory Limit: 162 MB
Submit: 3467  Solved: 1438
[Submit][Status][Discuss]

Description

  若是某個無向連通圖的任意一條邊至多隻出如今一條簡單迴路(simple cycle)裏,咱們就稱這張圖爲仙人掌
圖(cactus)。所謂簡單迴路就是指在圖上不重複通過任何一個頂點的迴路。php

 

  舉例來講,上面的第一個例子是一張仙人圖,而第二個不是——注意到它有三條簡單迴路:(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,可是咱們
保證全部的邊都會出如今某條路徑上,並且不會重複出如今兩條路徑上,或者在一條路徑上出現兩次。spa

Output

  只需輸出一個數,這個數表示仙人圖的直徑長度。code

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 8
10 1
10 1 2 3 4 5 6 7 8 9 10

Sample Output

8
9

HINT

 

對第一個樣例的說明:如圖,6號點和12號點的最短路徑長度爲8,因此這張圖的直徑爲8。blog


 


【注意】使用Pascal語言的選手請注意:你的程序在處理大數據的時候可能會出現棧溢出。

若是須要調整棧空間的大小,能夠在程序的開頭填加一句:{$M 5000000},其中5000000即

指代棧空間的大小,請根據本身的程序選擇適當的數值。

 

Source

仙人掌DP隊列

對於這種題的套路就是先考慮只是一棵樹的狀況,再特判環上的狀況ip

若是是裸的樹,咱們用$f[i]$表示$i$號節點對應的最長鏈的長度,而後枚舉任意兩個點之間的邊,進行更新get

若是出現了環怎麼辦?string

考慮若是是在環上,一樣是用環上的兩點去更新的答案,it

任意兩點對答案的貢獻爲$f[i]+f[j]+dis(i,j)$,$dis(i,j)$表示$i,j$兩點間的最短路徑

在環上,任意兩點間存在兩條路徑,此時有兩種處理方法:

1.正着來一遍再倒着來一遍

2.拆環成鏈

而後用單調隊列維護一下就好了。

 

#include<cstdio> #include<cstring> #include<algorithm>
using namespace std; const int MAXN = 1e6 + 10, INF = 1e9 + 10; inline int read() { char c = getchar(); int x = 0, f = 1; while(c < '0' || c > '9') {if(c == '-') f = -1; c = getchar();} while(c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar(); return x * f; } struct Edge { int u, v, nxt; }E[MAXN << 1]; int head[MAXN], num = 1; inline void AddEdge(int x, int y) { E[num] = (Edge){x, y, head[x]}; head[x] = num++; } int N, M; int fa[MAXN], dfn[MAXN], low[MAXN], Index, deep[MAXN]; int f[MAXN], ans = 0; int a[MAXN << 1], Q[MAXN]; void DP(int x, int y) { int tot = 0; for(int i = y; i != x; i = fa[i]) a[++tot] = i; a[++tot] = x; reverse(a + 1, a + tot + 1); for(int i = 1; i <= tot; i++) a[i + tot] = a[i]; int h = 1, t =0; for(int i = 1; i <= (tot << 1); i++) { while(h <= t && i - Q[h] > tot / 2) h++; if(h <= t) ans = max(ans, f[a[i]] + f[a[Q[h]]] + i - Q[h]); while(h <= t && f[a[Q[t]]] - Q[t] < f[a[i]] - i) t--; Q[++t] = i; } for(int i = y; i != x; i = fa[i]) f[x] = max(f[x], f[i] + min(deep[i] - deep[x], deep[y] - deep[i] + 1)); } void Tarjan(int x, int _fa) { fa[x] = _fa; dfn[x] = low[x] = ++Index; deep[x] = deep[_fa] + 1; for(int i = head[x], v; i != -1; i = E[i].nxt) { if((v = E[i].v) == _fa) continue; if(!dfn[v]) Tarjan(v, x), low[x] = min(low[x], low[v]); else low[x] = min(low[x], dfn[v]); if(dfn[x] < low[v]) ans = max(ans, f[x] + f[v] + 1), f[x] = max(f[x], f[v] + 1); //why is dfn?
        if(fa[v] != x && dfn[v] > dfn[x]) DP(x, v); } } int main() { #ifdef WIN32 freopen("a.in", "r", stdin); #else
#endif memset(head, -1, sizeof(head)); N = read(); M = read(); for(int i = 1; i <= M; i++) { int K = read(), pre = read(); for(int j = 2; j <= K; j++) { int now = read(); AddEdge(pre, now); AddEdge(now, pre), pre = now; } } Tarjan(1, 0); printf("%d\n", ans); return 0; }
相關文章
相關標籤/搜索