BZOJ 1023: [SHOI2008]cactus仙人掌图 | 在仙人掌上跑DP
题目:
求仙人掌直径
http://www.lydsy.com/JudgeOnline/problem.php?id=1023
题解:
首先给出仙人掌的定义:满足所有的边至多在一个环上的无向联通图
我们先考虑一下如何求一棵树的直径
1.维护以每个节点的为根的子树中,以根为一条路径端点的最长路和次长路,
#include<cstdio> #include<algorithm> #include<cstring> #define N 50005 using namespace std; inline int read() { int ret=0,neg=1;char j=getchar(); for (;j>'9' || j<'0';j=getchar()) if (j=='-') neg=-1; for (;j>='0' && j<='9';j=getchar()) ret=ret*10+j-'0'; return ret*neg; } int n,m,ecnt,ind,ans; int head[N],deep[N],f[N],low[N],dfn[N],fa[N]; int a[2*N],q[2*N],l,r; struct adj{int v,nxt;}e[40*N]; void add(int u,int v) { e[++ecnt].v=v;e[ecnt].nxt=head[u];head[u]=ecnt; e[++ecnt].v=u;e[ecnt].nxt=head[v];head[v]=ecnt; } void dp(int root,int x) { int tot=deep[x]-deep[root]+1; for (int i=x;i!=root;i=fa[i]) a[tot--]=f[i]; a[tot]=f[root]; tot=deep[x]-deep[root]+1; for (int i=1;i<=tot;i++) a[i+tot]=a[i]; q[1]=1;l=r=1; for (int i=2;i<=2*tot;i++) { while (l<=r && i-q[l]>tot/2) l++; ans=max(ans,a[i]+i+a[q[l]]-q[l]); while (l<=r && a[q[r]]-q[r]<=a[i]-i) r--; q[++r]=i; } for (int i=2;i<=tot;i++) f[root]=max(f[root],a[i]+min(i-1,tot-i+1)); } void dfs(int x) { low[x]=dfn[x]=++ind; for (int i=head[x],v;i;i=e[i].nxt) if (e[i].v!=fa[x]) { if (!dfn[v=e[i].v]) { fa[v]=x;deep[v]=deep[x]+1; dfs(v); low[x]=min(low[x],low[v]); } else low[x]=min(low[x],dfn[v]); if (dfn[x]<low[v]) ans=max(ans,f[x]+f[v]+1),f[x]=max(f[x],f[v]+1); } for (int i=head[x],v;i;i=e[i].nxt) if (fa[v=e[i].v]!=x && dfn[x]<dfn[v]) dp(x,v); } int main() { n=read();m=read(); for (int i=1;i<=m;i++) { int k=read(),x=read(); for (int i=2;i<=k;i++) { int y=read();add(x,y);x=y; } } dfs(1); printf("%d\n",ans); return 0; }