题目描述:
题解:
仙人掌$tarjan$。
维护这个点向下走的最长链长度。
搞到环的话拆开环用单调栈扫。
代码:
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
#include<cstdio> #include<cstring> #include<algorithm> using namespace std; const int N = 50050; const int M = 100050; template<typename T> inline void read(T&x) { T f = 1,c = 0;char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} while(ch>='0'&&ch<='9'){c=c*10+ch-'0';ch=getchar();} x = f*c; } int n,m,hed[N],cnt=1; struct EG { int to,nxt; }e[2*M]; void ae(int f,int t) { e[++cnt].to = t; e[cnt].nxt = hed[f]; hed[f] = cnt; } void build() { int a,b=-1,c; read(a); while(a--) { read(c); if(~b)ae(b,c),ae(c,b); b=c; } } int dp[N],dep[N],low[N],tim; int sta[N],tl,ans; int r[2*N],tr,s[N],h,l; void sol() { h = 1,l = 0; int lim = tr/2; for(int i=1;i<=lim;i++) { while(h<=l&&dp[r[s[l]]]+s[l]<=dp[r[i]]+i)l--; s[++l] = i;r[tr+i]=i; } for(int i=1;i<=tr;i++) { while(h<=l&&s[h]<=i)h++; while(h<=l&&dp[r[s[l]]]+s[l]<=dp[r[i+lim]]+i+lim)l--; s[++l] = i+lim; ans = max(ans,dp[r[i]]-i+dp[r[s[h]]]+s[h]); } for(int i=2;i<=tr;i++) dp[r[1]]=max(dp[r[1]],min(i-1,tr+1-i)+dp[r[i]]); } void tarjan(int u,int f) { dp[u] = 0; dep[u] = low[u] = ++tim; sta[++tl] = u; for(int j=hed[u];j;j=e[j].nxt) { int to = e[j].to; if(to==f)continue; if(!dep[to]) { tarjan(to,u); low[u] = min(low[u],low[to]); if(low[to]>dep[u]) { ans = max(ans,dp[u]+dp[to]+1); dp[u] = max(dp[u],dp[to]+1); tl--; }else if(low[to]==dep[u]) { r[tr=1]=u; while(sta[tl+1]!=to)r[++tr]=sta[tl--]; sol(); } }else low[u] = min(low[u],dep[to]); } } int main() { read(n),read(m); while(m--)build(); tarjan(1,-1); printf("%d\n",ans); return 0; }