来自FallDream的博客,未经允许,请勿转载,谢谢。
给定一个仙人掌,求最大的两点最短距离。 n<=50000
好难啊根本不会做。
题解:先强行dfs把图拆成树,然后我们用f[i]表示i的子树中以i为起点的最长链长度。
图中的每条边不是桥就是环上的边,对于一座桥,我们先更新一下答案,然后有f[i]=max(f[i],f[j]+1);
对于一个环,我们只在深度最低的点处理,用环上所有点的f值更新它的f。然后我们考虑最优解在环上的情况,这时我们把环拆链,然后复制一条接起来,在这上面有ans=max(f[i]+f[j]+dis(i,j))。
假设一条链标号1,2...tot...2tot,我们枚举i,这时候dis(i,j)=i-j,j比k优当且仅当f[i]+f[j]+i-j>f[i]+f[k]+i-k,也就是f[j]-j>f[k]-k,又因为我们在环上,所以长度不能超过tot/2,这个用一个单调队列维护就行了。
顺便,判断桥的方法是low[j]>dfn[i],即j不能翻到i或之上的点,无向图判环要忽略到父亲的边。
#include<iostream> #include<cstdio> #include<cstring> #define MN 50000 using namespace std; inline int read() { int x = 0 , f = 1; char ch = getchar(); while(ch < '0' || ch > '9'){ if(ch == '-') f = -1; ch = getchar();} while(ch >= '0' && ch <= '9'){x = x * 10 + ch - '0';ch = getchar();} return x * f; } int q[MN+5],dn=0,tot=0,a[2*MN+5],fa[MN+5],top,tail; int head[MN+5],n,cnt=0,m,k,f[MN+5],ans=0,dfn[MN+5],low[MN+5]; struct edge{int to,next;}e[MN*10+5]; void ins(int f,int t) { e[++cnt]=(edge){t,head[f]};head[f]=cnt; e[++cnt]=(edge){f,head[t]};head[t]=cnt; } void dp(int rt,int last) { for(tot=0;last!=rt;last=fa[last]) a[++tot]=f[last]; a[++tot]=f[rt]; for(int i=1;i<=tot;i++)a[i+tot]=a[i]; q[top=tail=1]=1; for(int i=2;i<=tot<<1;i++) { while(i-q[tail]>tot/2) tail++; ans=max(ans,a[i]+a[q[tail]]+i-q[tail]); while(top>=tail&&a[i]-i>=a[q[top]]-q[top]) --top; q[++top]=i; } for(int i=1;i<=tot;i++) f[rt]=max(f[rt],a[i]+min(tot-i,i)); } void tarjan(int x,int fat) { low[x]=dfn[x]=++dn;fa[x]=fat; for(int i=head[x];i;i=e[i].next) { if(!dfn[e[i].to]) tarjan(e[i].to,x),low[x]=min(low[x],low[e[i].to]); else if(e[i].to!=fat) low[x]=min(low[x],dfn[e[i].to]); if(e[i].to!=fat&&dfn[x]<low[e[i].to]) ans=max(ans,f[x]+f[e[i].to]+1),f[x]=max(f[x],f[e[i].to]+1); } for(int i=head[x];i;i=e[i].next) if(fa[e[i].to]!=x&&dfn[e[i].to]>dfn[x]) dp(x,e[i].to); } int main() { n=read();k=read(); for(int i=1;i<=k;i++) { int m=read(),pre=read(); for(int j=2;j<=m;j++) { int x=read(); ins(x,pre);pre=x; } } tarjan(1,0); printf("%d ",ans); return 0; }