BZOJ 1023: [SHOI2008]cactus仙人掌图 (仙人掌,树形DP,单调队列)
对于树边直接转移,然后按照套路拆环,在环上做一个单调队列 DP.
一种方案虽然在一个换上可能算不了,但是一定可以在另一个环上计算到.
仙人掌上 DP 的套路就是遇到树边就按照树上做,遇到环就拆环,做一个环形 DP.
code:
#include <bits/stdc++.h> #define N 200006 #define M 400006 #define setIO(s) freopen(s".in","r",stdin) using namespace std; int ans,n,m; int hd[N],to[M],nex[M],edges,tim,top; int tot,a[N],q[N],fa[N],low[N],dfn[N],dep[N],f[N]; void add(int u,int v) { nex[++edges]=hd[u],hd[u]=edges,to[edges]=v; } void calc(int root,int x) { top=dep[x]-dep[root]+1; for(int i=x;i!=root;i=fa[i]) a[top--]=f[i]; a[1]=f[root]; top=dep[x]-dep[root]+1; for(int i=1;i<=top;++i) a[i+top]=a[i]; int l=1,r=1; q[r]=1; for(int i=2;i<=top*2;++i) { while(l<=r&&i-q[l]>top/2) ++l; ans=max(ans,a[q[l]]+a[i]+i-q[l]); while(l<=r&&a[q[r]]-q[r]<=a[i]-i) --r; q[++r]=i; } for(int i=2;i<=top;++i) f[root]=max(f[root],a[i]+min(top-i+1,i-1)); } void dfs(int x,int ff) { dfn[x]=low[x]=++tim; fa[x]=ff,dep[x]=dep[ff]+1; for(int i=hd[x];i;i=nex[i]) { int v=to[i]; if(v==ff) continue; if(!dfn[v]) { dfs(v,x); 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=hd[x];i;i=nex[i]) { int v=to[i]; if(v==ff) continue; if(fa[v]!=x&&dfn[x]<dfn[v]) calc(x,v); } } int main() { // setIO("input"); scanf("%d%d",&n,&m); for(int i=1;i<=m;++i) { int k,last; scanf("%d%d",&k,&last); for(int j=1;j<k;++j) { int x; scanf("%d",&x); add(x,last),add(last,x); last=x; } } dfs(1,0); printf("%d\n",ans); return 0; }