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;
}

浙公网安备 33010602011771号