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,你的任务是求出给定的仙人图的直径。
早就开始做了。。。最近 czm 提起才想到。。。我觉得这是一道很好的题目。。。
首先求点双连通分量,然后对于每一个环 dp,可能的答案为 max{dis[i] + dis'[j], dis[x] + dist(x, y) + dis[y]}, dis 表示搜索树向下的最长路,dis‘表示第二长路(临时变量记之即可),dist(x, y) 表示仙人掌环上两点间的最短距离。
dp 就单(Ou)调(Shao)队列水过了。。。tarjan 什么的也就水过了。。。
然后爆栈什么的调了我一两个小时。。。最后删掉 dfs 中 3 个临时变量才过。。。
bzoj rank 10,貌似不是很颓。。。
Code :
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <map>
#include <set>
#include <queue>
#include <algorithm>
using namespace std;
#define FOR(i, j, k) for (i = (j); i <= (k); ++ i)
#define ROF(i, j, k) for (i = (j); i >= (k); -- i)
#define FER(i, j, k) for (i = j[k]; i; i = i->n)
#define maxn 50005
struct da{int t; da * n;};
da das[maxn * 4], * adj = das + 1, * edge[maxn];
int n, m, dfnn, tot, ans, i, j, k;
int dfn[maxn], low[maxn], dis[maxn];
da * sta[maxn * 2], ** top = sta;
int a[maxn * 2], q[maxn * 2], h, t;
void up(int & i, int j) {if (j > i) i = j;}
void down(int & i, int j) {if (j < i) i = j;}
void solve(int k)
{
int i; q[h = t = 1] = 1;
FOR (i, 2, tot)
{
while (i - q[h] > k) ++ h;
up(ans, dis[a[q[h]]] - q[h] + dis[a[i]] + i);
while (h <= t && dis[a[i]] - i > dis[a[q[t]]] - q[t]) -- t;
q[++ t] = i;
}
}
void dfs(int u, int fa)
{
da * e; int l = 0;
dfn[u] = low[u] = ++ dfnn;
FER (e, edge, u) if (e->t != fa)
if (dfn[e->t]) down(low[u], dfn[e->t]);
else
{
* (++ top) = e, dfs(e->t, u), down(low[u], low[e->t]);
if (low[e->t] > dfn[u])
{
-- top;
if (dis[e->t] + 1 > dis[u])
l = dis[u], dis[u] = dis[e->t] + 1;
else if (dis[e->t] + 1 > l)
l = dis[e->t] + 1;
}
else if (low[e->t] == dfn[u])
{
tot = j = 0;
do a[++ tot] = (* top)->t; while (* (top --) != e);
FOR (i, 1, tot)
{
up(j, dis[a[i]] + min(i, tot + 1 - i));
a[i + tot + 1] = a[i];
}
a[++ tot] = u, k = tot >> 1, tot = (tot << 1) - 1;
solve(k);
if (j > dis[u]) l = dis[u], dis[u] = j;
else if (j > l) l = j;
}
}
up(ans, l + dis[u]);
}
void link(int i, int j)
{
* (++ adj) = (da) {j, edge[i]}, edge[i] = adj;
* (++ adj) = (da) {i, edge[j]}, edge[j] = adj;
}
int main()
{
freopen("cactus.in", "r", stdin);
freopen("cactus.out", "w", stdout);
int u, v;
scanf("%d%d", & n, & m);
FOR (i, 1, m)
{
scanf("%d%d", & k, & u);
FOR (j, 2, k) scanf("%d", & v), link(u, v), u = v;
}
dfs(1, 0);
printf("%d\n", ans);
return 0;
}