题目链接
题意
求仙人掌直径
思路
圆方树,树形dp,dp[u] 表示以u为根节点最长链
建圆方树,将点双判断仙人掌改改就好
原点到原点,正常dp
原点到方点,正常dp
方点到原点,圆点各点加上方点父亲节点应该是一个环,然后对这个环用基环树求直径(单调队列),但是注意复制序列时方节点父亲并没有被考虑,判断一下。
建树
加粗是方点,方点的儿子之间距离按遍历顺序考虑,而不是图上的简单路径权值和
坑点
考虑序列应该如图
a是实际存在但不在序列中的点
俩数据
hack 建圆方树后不考虑方节点的父亲节点,进行环形dp的
8
3
7 1 2 3 4 5 6 2
2 7 3
2 8 5
ac:4
8
3
7 1 2 3 4 5 6 2
2 7 3
2 8 6
ac:4
代码
#include <bits/stdc++.h>
using namespace std;
const int N = 50005;
int n, m;
vector<int> e[N];
int dfn[N], low[N], tot;
int sta[N], top;
vector<int> T[N<<1];
int ndc;
void tarjan(int u, int fa) {
low[u] = dfn[u] = ++tot;
sta[++top] = u;
for(auto v : e[u]) if(v ^ fa) {
if(!dfn[v]) {
tarjan(v,u);
low[u] = min(low[u], low[v]);
if(low[v] > dfn[u]) T[u].push_back(v), --top;
if(low[v] == dfn[u]) {
T[u].push_back(++ndc);
for(int x = -1; x^v;) T[ndc].push_back(x = sta[top--]);
}
}
else low[u] = min(low[u], dfn[v]);
}
}
int dp[N<<1], ans, q[N], a[N<<1];
void dfs(int u) {
for(auto v : T[u]) dfs(v);
if(u <= n) {
for(auto v : T[u]) {
ans = max(ans, dp[u]+dp[v]+1);
dp[u] = max(dp[u], dp[v]+1);
}
}
else {
int l = 1, r = 0, cnt = 0;
for(auto v : T[u]) a[++cnt] = v;
for(int i = 1; i <= cnt; ++i) a[cnt+i] = a[i];
for(int i = 1; i <= 2*cnt; ++i) {
// 两点之间的距离为这两点之间最短路径的距离,记得考虑消失的一个基环点即u父亲
// 这部分代码中三目运算都是考虑消失的方节点父亲
while(l <= r && i-q[l]+(((i-1)/cnt != (q[l]-1)/cnt)?1:-1) > q[l]+cnt-i) ++l;
// if(l <= r) ans = max(ans, dp[a[i]]+dp[a[q[l]]]+i-q[l]);
if(l <= r) ans = max(ans, dp[a[i]]+dp[a[q[l]]]+i-q[l]+((i>cnt&&q[l]<=cnt)?1:0));
while(l <= r && dp[a[i]]-i >= dp[a[q[r]]]-q[r]) --r;
q[++r] = i;
}
for(int i = 1; i <= cnt; ++i) dp[u] = max(dp[u], dp[a[i]]+min(i-1,cnt-i));
}
}
int main() {
scanf("%d%d",&n,&m);
ndc = n;
for(int i = 1; i <= m; ++i) {
int tmp, u, v;
scanf("%d%d",&tmp,&u);
while(--tmp) {
scanf("%d",&v);
e[u].push_back(v);
e[v].push_back(u);
u = v;
}
}
tarjan(1,0);
dfs(1);
printf("%d\n",ans);
return 0;
}