【BZOJ1023】仙人掌图(SHOI2008)-圆方树+DP+单调队列
测试地址:仙人掌图
做法:本题需要用到圆方树+DP+单调队列。
看到仙人掌就想到圆方树。我们可以把仙人掌上的DP转化为圆方树上的DP。
首先对于LCA在圆点上的点对,它们之间的距离可以直接通过圆方树的边权算出,所以这一部分直接用树上求直径的DP做就行了。
关键是LCA在方点上的点对,这样的点对之间的最短路径与这个方点所指的环的交是一条链,那么这条路径就分成两个部分:在某点子树中的部分和在环上的部分。某点到子树中最长的链长我们可以DP求出,而处理环的话我们可以类比环套树求直径的方法用单调队列优化DP处理,即对环上的每个点,求在它顺时针半环长的区间内的点的子树中与这个点的子树中点对距离的最大值。这样处理一个环的时间复杂度是的,那么整个算法的时间复杂度就是的,可以通过此题。
我傻逼的地方:一开始写的仙人掌模板是错的……我也不知道为什么能A掉一题……
以下是本人代码:
#include <bits/stdc++.h>
using namespace std;
const int N=50010;
const int M=N<<2;
int n,m,tote=0,totpbc,first[N]={0},firsted[N<<1]={0},tot=0,fa[N],ans=0;
int st[N],top=0,dfn[N],low[N],tim=0;
int dp[N<<1]={0},q[N<<1],pos[N<<1],cir[N<<1],h,t;
bool vis[N]={0},inst[N]={0};
struct edge
{
int v,next,w,id;
}e[M],ed[M];
void insert(int a,int b,int id)
{
e[++tot].v=b;
e[tot].next=first[a];
e[tot].id=id;
first[a]=tot;
}
void inserted(int a,int b,int w)
{
ed[++tot].v=b;
ed[tot].next=firsted[a];
ed[tot].w=w;
firsted[a]=tot;
}
void combine(int x,int y)
{
int now=y;
cir[0]=0;
while(now!=x)
{
cir[++cir[0]]=now;
now=fa[now];
}
cir[++cir[0]]=x;
for(int i=1;i<=cir[0];i++) cir[cir[0]+i]=cir[i];
h=1,t=0;
for(int i=1;i<=(cir[0]>>1);i++)
{
while(h<=t&&dp[q[t]]+pos[t]<=dp[cir[i]]+i) t--;
q[++t]=cir[i];
pos[t]=i;
}
for(int i=(cir[0]>>1)+1;i<=cir[0]+(cir[0]>>1);i++)
{
now=i-(cir[0]>>1);
while(h<=t&&pos[h]<=now) h++;
while(h<=t&&dp[q[t]]+pos[t]<=dp[cir[i]]+i) t--;
q[++t]=cir[i];
pos[t]=i;
ans=max(ans,dp[q[h]]+pos[h]+dp[cir[now]]-now);
}
inserted(x,++totpbc,0);
for(int i=1;i<cir[0];i++)
{
inserted(totpbc,cir[i],min(i,cir[0]-i));
dp[totpbc]=max(dp[totpbc],dp[cir[i]]+min(i,cir[0]-i));
}
}
void tarjan(int v,int laste)
{
vis[v]=inst[v]=1;
dfn[v]=low[v]=++tim;
st[++top]=v;
int now=top;
for(int i=first[v];i;i=e[i].next)
if (e[i].id!=laste)
{
if (!vis[e[i].v])
{
fa[e[i].v]=v;
tarjan(e[i].v,e[i].id);
if (low[e[i].v]>dfn[v])
{
top--;
inst[e[i].v]=0;
inserted(v,e[i].v,1);
}
if (low[e[i].v]==dfn[v])
{
for(int i=top;i>now;i--)
inst[st[i]]=0;
top=now;
}
low[v]=min(low[v],low[e[i].v]);
}
else if (inst[e[i].v]) low[v]=min(low[v],dfn[e[i].v]);
}
for(int i=first[v];i;i=e[i].next)
if (fa[e[i].v]!=v&&dfn[v]<dfn[e[i].v]) combine(v,e[i].v);
for(int i=firsted[v];i;i=ed[i].next)
{
ans=max(ans,dp[v]+dp[ed[i].v]+ed[i].w);
dp[v]=max(dp[v],dp[ed[i].v]+ed[i].w);
}
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=m;i++)
{
int k,last=0,a;
scanf("%d",&k);
while(k--)
{
scanf("%d",&a);
if (last) insert(a,last,++tote),insert(last,a,tote);
last=a;
}
}
totpbc=n;
tot=0;
fa[1]=0;
tarjan(1,0);
printf("%d",ans);
return 0;
}