仙人掌学习笔记

定义:

  • 是一个连通图。
  • 每一条边最多属于一个简单环。

做法:

DFS树

根据 \(tarjan\) 的过程,得到一个\(DFS\)树,同时也可以可得到每个点属于哪一个环(或不属于任何一个环)。对于只属于树的边,进行树形DP;对于环上的边,在环上DP。适用于仅需操作一次的简单仙人掌DP。
代码框架如下(以求仙人掌直径为例):

void dp(int x,int y) {//环上DP
    cnt=0; for(int i=y;i!=x;i=fa[i]) a[++cnt]=f[i]; a[++cnt]=f[x];//处理出环上的点
    for(int i=1;i<=cnt/2;i++)   swap(a[i],a[cnt-i+1]);
    for(int i=cnt+1;i<=cnt+cnt/2;i++) a[i]=a[i-cnt];//复制原串
    q[F=E=1]=1;//单调队列优化DP
    for(int i=2;i<=cnt+cnt/2;i++) {
        if(F<=E&&i-q[F]>cnt/2)  ++F;
        ans=Max(ans,a[q[F]]+a[i]+i-q[F]);
        while(F<=E&&a[i]-i>=a[q[E]]-q[E]) --E;
        q[++E]=i;
    }
    for(int i=2;i<=cnt;i++) f[x]=Max(f[x],a[i]+Min(i-1,cnt-i+1));//更新
}

void dfs(int u,int ff) {//模拟tarjan的过程
    dfn[u]=low[u]=++idx,f[u]=0;//f[i]为DP的状态
    for(int i=0;i<e[u].size();i++) {//枚举每条出边
        if(e[u][i]==ff) continue;//判掉父亲
        if(!dfn[e[u][i]])//未访问过,加边建DFS树
            fa[e[u][i]]=u,dfs(e[u][i],u),low[u]=Min(low[u],low[e[u][i]]);
        else low[u]=Min(low[u],dfn[e[u][i]]);//更新该节点能回到的最早的点
        if(low[e[u][i]]>dfn[u])//进行简单的树形DP
            ans=Max(ans,f[u]+f[e[u][i]]+1),f[u]=Max(f[u],f[e[u][i]]+1);
    }
    for(int i=0;i<e[u].size();i++)//查找仙人掌上的儿子但不是DFS树上的儿子的点,即形成环
        if(e[u][i]!=ff&&fa[e[u][i]]!=u&&dfn[e[u][i]]>dfn[u]) dp(u,e[u][i]);//进行环上DP
}

例题:
SHOI2008 cactus仙人掌图
小C的独立集
HNOI2009 无归岛


圆方树

(听说广义圆方树可以处理一些图的问题,如 【CF Round #278】Tourists, APIO2018 Duathlon 铁人两项)

留坑待填

转载于:https://www.cnblogs.com/daniel14311531/p/10246731.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值