1. 程式人生 > >仙人掌學習筆記

仙人掌學習筆記

定義:

  • 是一個連通圖。
  • 每一條邊最多屬於一個簡單環。

做法:

DFS樹

根據 \(tarjan\) 的過程,得到一個\(DFS\)樹,同時也可以可得到每個點屬於哪一個環(或不屬於任何一個環),適用於僅需操作一次的簡單仙人掌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的獨立集


圓方樹

(聽說廣義圓方樹可以處理一些圖的問題,如 【CF Round #278】Tourists, APIO2018 Duathlon 鐵人兩項)

留坑待填