bzoj1023 [SHOI2008]cactus仙人掌图 树形DP+单调队列

来源:互联网 发布:董易奇八字软件 编辑:程序博客网 时间:2024/05/20 23:02

题意:给一颗仙人掌求直径。
经典好题。
一开始naive的想以为缩点以后直接求,想了想感觉自己是傻子。。块内的根本无法统计。
大概能想到DP求解,但是单调队列真心被震惊到了= =
设f[x],表示以x为起点(从上往下)的最长路径,对于树边/非树边分别转移,树边当让直接转移了,主要是非树边,非树边就是环上边,我只用环上的点更新f[x](x为环上深度最小点),这个需要DP。
树边情况,对于f[x],f[x]=max(f[v]+1)vson
非树边情况:
已知x为环上深度最小点,所以这个环的其余点,只要和x连边,都可以转移到x。
f[x]=max(f[v]+dis(x,v)),注意x和v在一个环内,这个式子就不难理解了。
问题是我们肯定不能够暴力枚举环内点,注意到dis(i,j),可以表示成min(j-i,n-i+j),为了去掉min,我们把环复制一遍,每次只考虑半个环长度的dp,然后就可以把式子化成max(f[i]+f[j]+ji),那么现在单调队列处理max(f[j]+j),这样就很简单了。
真是神题。。

#include<cstdio>#include<algorithm>#include<cstring>#define fo(i,a,b) for(int i=a;i<=b;i++)#define fd(i,a,b) for(int i=a;i>=b;i--)using namespace std;const int N=1e5+5;const int M=5e6+5;int n,m;int a[N],head[N],next[M],go[M];int low[N],dfn[N],vis[N],sta[N],tot,cnt;int fa[N],dep[N],f[N],q[N],ans;int read(){    int x=0;char ch=getchar();    while (ch<'0'||ch>'9')ch=getchar();    while (ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}    return x;}inline void add(int x,int y){    go[++tot]=y;    next[tot]=head[x];    head[x]=tot;}inline void dp(int x,int y){    int tot=dep[y]-dep[x]+1;    int t=1,w=1;    for(int i=y;i!=x;i=fa[i])a[tot--]=f[i];    a[1]=f[x];tot=dep[y]-dep[x]+1;    q[1]=1;    fo(i,1,tot)a[i+tot]=a[i];    fo(i,2,2*tot)    {        if (i-q[t]>tot/2)t++;        ans=max(ans,a[i]+i+a[q[t]]-q[t]);        while (t<=w&&a[i]-i>=a[q[w]]-q[w])w--;        q[++w]=i;    }    fo(i,2,tot)f[x]=max(f[x],a[i]+min(i-1,tot-i+1));}inline void dfs(int x){    dfn[x]=low[x]=++cnt;    for(int i=head[x];i;i=next[i])    {        int v=go[i];        if (v==fa[x])continue;        if (!dfn[v])        {            fa[v]=x;            dep[v]=dep[x]+1;            dfs(v);        }        low[x]=min(low[x],low[v]);        if (low[v]>dfn[x])        ans=max(ans,f[v]+f[x]+1),f[x]=max(f[x],f[v]+1);    }    for(int i=head[x];i;i=next[i])    {        int v=go[i];        if (dfn[v]>dfn[x]&&fa[v]!=x)dp(x,v);    }}int main(){    n=read(),m=read();    fo(i,1,m)    {        int s=read(),last=0,x;        fo(j,1,s)        {            x=read();            if (last)add(last,x),add(x,last);            last=x;        }    }    dfs(1);    printf("%d\n",ans);}
原创粉丝点击