BZOJ1023: [SHOI2008]cactus仙人掌图(单调队列优化DP)

来源:互联网 发布:网络新媒体概论微盘 编辑:程序博客网 时间:2024/05/20 22:36

传送门

题意:
求一颗仙人掌的直径。

题解:
DP。

首先建出图的DFS树。

因为是仙人掌图,所以每个环必定有一个dfs序最小的点,连接着若干条后向边和树边,表示环上的边或者割边。

记录f[i]表示dfs树上以i为根的子树(子图)中最长链。考虑DP:

1.对于一条割边, 对答案的影响为f[i]+f[v]+1,之后直接f[i]=max{f[i],f[v]+1}
2.对于环边先不处理,等到dfs完所有子树后重新枚举边查找环上的儿子。(因为是在dfs树上所以连接的儿子一定是环上最后一个点。)
在多个环上分别dp。

对于环的dp:
首先,只需更新dfs树中最高点的f[i]。因为dfs树上对前面的点有影响的只有最高点,只需用最高点储存环上信息。直接枚举所有点,f[i]=max(f[i],maxjcir(i){f[j]+dis(i,j)})

其次,环上的两个不是最高点的点可能对答案产生影响,就像一个环上挂了许多子树,每个子树的最长链为f[j],求maxj1,j2cir(i){f[j1]+f[j2]+dis(i,j)}。这是一个经典的单调队列优化dp。但是,与求基环外向树直径不同的是,这里的dis(i,j)取环上的最小值,所以只能从i(n/2)+1开始枚举。

#include<bits/stdc++.h>using namespace std;streambuf *ib,*ob;inline void init(){    ios::sync_with_stdio(false);    cin.tie(NULL);cout.tie(NULL);    ib=cin.rdbuf();ob=cout.rdbuf();}inline int read(){    char ch=ib->sbumpc();int i=0,f=1;    while(!isdigit(ch)){if(ch=='-')f=-1;ch=ib->sbumpc();}    while(isdigit(ch)){i=(i<<1)+(i<<3)+ch-'0';ch=ib->sbumpc();}    return i*f;}int buf[50];inline void W(int x){    if(!x){ob->sputc('0');return;}    if(x<0){ob->sputc('-');x=-x;}    while(x){buf[++buf[0]]=x%10;x/=10;}    while(buf[0]){ob->sputc(buf[buf[0]--]+'0');}}const int Maxn=5e4+50;int n,m,f[Maxn],dfn[Maxn],ind,low[Maxn],fa[Maxn],dep[Maxn],ans;vector<int>edge[Maxn];inline void dp(int rt,int x){    static int a[Maxn*2],que[Maxn*2],head,tail,cnt;    cnt=dep[x]-dep[rt]+1;    for(int i=cnt,y=x;i>=1;i--,y=fa[y])a[i]=y;    int lim=cnt/2;    memcpy(a+cnt+1,a+1,sizeof(int)*lim);    que[head=tail=1]=1;    int t=cnt;cnt+=lim;    for(int i=2;i<=cnt;i++){        while(head<=tail&&que[head]<i-lim)head++;        ans=max(ans,f[a[i]]+i+f[a[que[head]]]-que[head]);        while(head<=tail&&f[a[que[tail]]]-que[tail]<=f[a[i]]-i)tail--;        que[++tail]=i;    }    for(int i=2;i<=t;i++)        f[rt]=max(f[rt],f[a[i]]+min(i-1,t-i+1));}inline void dfs(int now,int father){    dep[now]=dep[father]+1;low[now]=dfn[now]=++ind;fa[now]=father;    for(int e=edge[now].size()-1;e>=0;e--)    {        int v=edge[now][e];        if(v==father)continue;        if(!dfn[v]){            dfs(v,now);            low[now]=min(low[now],low[v]);        }        else low[now]=min(low[now],dfn[v]);        if(dfn[now]<low[v]){            ans=max(ans,f[now]+f[v]+1);            f[now]=max(f[now],f[v]+1);        }    }    for(int e=edge[now].size()-1;e>=0;e--)    {        int v=edge[now][e];        if(v==father)continue;        if(fa[v]!=now&&dfn[v]>dfn[now])        dp(now,v);    }}int main(){    init();n=read(),m=read();    for(int i=1;i<=m;i++)    {        int k=read(),a=read();        for(int i=2;i<=k;i++)        {            int b=read();            edge[a].push_back(b);            edge[b].push_back(a);            a=b;        }    }    dfs(1,0);    W(ans);ob->sputc('\n');}
阅读全文
0 0
原创粉丝点击