仙人掌 && 圆方树 && 虚树 总结
Part1 仙人掌
定义
仙人掌是满足以下两个限制的图:
- 图完全联通。
- 不存在一条边处在两个环中。
其中第二个限制让仙人掌的题做起来十分舒服。
仙人掌的基环DP
首先勾出一棵有根生成树。
那么树边上正常转移即可。
我们把返祖边形成的环归到环上深度最浅的点上,即环顶。
那么到环顶时,单独跑一遍关于环的\(DP\)即可。
一般写法为:
void dfs(RG int u,RG int From) {
dfn[u] = low[u] = ++ oo ; fa[u] = From ;
for(RG int i = head[u] ; i ; i = t[i].next) {
RG int v = t[i].to ;
if(!dfn[v]) dfs(v , u) , low[u] = min(low[u] , low[v]);
else if(v != From) low[u] = min(low[u] , dfn[v]) ;
if(low[v] > dfn[u]) 正常的树形DP(F(v) --> F(u))。
}
for(RG int i = head[u] ; i ; i = t[i].next)
if(fa[t[i].to] != u && dfn[t[i].to] > dfn[u]) 基环DP(u,v)。
}
分清楚\(low\)、\(dfn\)的含义即可。
由于记录了\(fa_u\),基环DP中的扣环也非常容易:
tot = 0 ;
for(RG int x = v; x ^ u; x = fa[x]) q[++tot] = x ;
q[++tot] = u ;
例题
例一:BZOJ4316 小\(C\)的独立集
题意:求仙人掌的最大独立集。
题解:做一遍正常的树形\(DP\),遇到环则把环拉出来单独做一遍。
例二:BZOJ1023 SHOI2008仙人掌图
题意:求仙人掌的直径。
题解:
同样的做正常树形\(DP\),碰到环顶则把环拉出来。
问题变为在环上选两个点,使其权值与距离和最大。显然单调队列即可。
Part2 圆方树
概况
圆方树是基于仙人掌的一种特殊数据结构。
其中圆点即图中原来的点,方点则代表一个点双。
我们沿用上面处理仙人掌\(DP\)的做法。
如果是生成树上的边,则直接相连。
否则,把环抠出来,为这个环新建一个方点,将环上的点与此方点连边。
板子与上面的仙人掌DP基本一样就不放了。
那么我们就可以在圆点上维护原本图单点信息,方点上维护点双信息了。