仙人掌學習筆記
阿新 • • 發佈:2019-01-09
定義:
- 是一個連通圖。
- 每一條邊最多屬於一個簡單環。
做法:
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 鐵人兩項)
留坑待填