Address
Solution
第一次做仙人掌图。- 主要难在环中 DP 值的处理。
注意题目中提到的性质:
仙人图上的每条边,或者是这张仙人图的桥,或者在且仅在一个简单回路里,两者必居其一。
所以当然先写个 Tarjan T a r j a n 啦 。
- Tarjan T a r j a n 也是在 DFS D F S ,考虑 DP D P 的转移。
- 记 f[x] f [ x ] 表示以点 x x 为起点的最长链长度, 为答案。
- 若
dfn[x]<low[y](存在边x→y)
d
f
n
[
x
]
<
l
o
w
[
y
]
(
存
在
边
x
→
y
)
,则
x,y
x
,
y
不在一个边双内:
ans=max{ans,f[x]+f[y]+1},f[x]=max{f[x],f[y]+1} a n s = max { a n s , f [ x ] + f [ y ] + 1 } , f [ x ] = max { f [ x ] , f [ y ] + 1 }
- 对于每个环,我们找到环中深度最小的点记为 rt r t ,环中第 i i 个点为 ,共有 q q 个点。
- 则 。
- 同时在环中任选两个点取最长链也能更新 ans a n s ,即 ans=max{f[xi]+i+f[xj]−j}(1≤j<i≤q) a n s = m a x { f [ x i ] + i + f [ x j ] − j } ( 1 ≤ j < i ≤ q ) 。
- 那么对于每个 xi x i ,找到 f[xj]−j f [ x j ] − j 最大的 j j 来更新显然最优,可以用单调队列优化成 ,因此总的时间复杂度 O(n+m) O ( n + m ) 。
Code
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cctype>
#include <algorithm>
using namespace std;
inline int get()
{
char ch; int res = 0; bool flag = false;
while (ch = getchar(), !isdigit(ch) && ch != '-');
(ch == '-' ? flag = true : res = ch ^ 48);
while (ch = getchar(), isdigit(ch))
res = res * 10 + ch - 48;
return flag ? -res : res;
}
const int N = 1e5 + 5, M = 1e7 + 5, L = 2e5 + 5;
int dfn[N], low[N], dep[N], fa[N], f[N], g[L], h[L];
int n, m, q, ans, tis;
struct Edge
{
int to; Edge *nxt;
}p[M], *lst[N], *P = p;
inline void Link(int x, int y)
{
(++P)->nxt = lst[x]; lst[x] = P; P->to = y;
(++P)->nxt = lst[y]; lst[y] = P; P->to = x;
}
inline void CkMin(int &x, int y) {if (x > y) x = y;}
inline void CkMax(int &x, int y) {if (x < y) x = y;}
inline int Min(int x, int y) {return x < y ? x : y;}
inline void solveDP(int rt, int x)
{
q = dep[x] - dep[rt] + 1;
for (int y = x; y != rt; y = fa[y], --q)
g[q] = f[y];
g[q] = f[rt];
q = dep[x] - dep[rt] + 1;
for (int i = 1; i <= q; ++i)
g[i + q] = g[i];
int tmp = q >> 1, t = 1, w = 0;
for (int i = 1, im = q << 1; i <= im; ++i)
{
while (t <= w && i - h[t] > tmp) ++t;
if (t <= w)
CkMax(ans, g[i] + g[h[t]] + i - h[t]);
while (t <= w && g[i] - i >= g[h[w]] - h[w]) --w;
h[++w] = i;
}
for (int i = 2; i <= q; ++i)
CkMax(f[rt], g[i] + Min(i - 1, q - i + 1));
}
inline void Tarjan(int x)
{
dfn[x] = low[x] = ++tis;
for (Edge *e = lst[x]; e; e = e->nxt)
{
int y = e->to;
if (y == fa[x]) continue;
if (!dfn[y])
{
fa[y] = x;
dep[y] = dep[x] + 1;
Tarjan(y);
CkMin(low[x], low[y]);
}
else CkMin(low[x], dfn[y]);
if (dfn[x] < low[y])
{
CkMax(ans, f[x] + f[y] + 1);
CkMax(f[x], f[y] + 1);
}
}
for (Edge *e = lst[x]; e; e = e->nxt)
{
int y = e->to;
if (fa[y] != x && dfn[x] < dfn[y])
solveDP(x, y);
}
}
int main()
{
n = get(); m = get(); int k, x, y;
while (m--)
{
k = get(); x = get();
while (--k)
{
y = get();
Link(x, y);
x = y;
}
}
Tarjan(1);
printf("%d\n", ans);
}