https://darkbzoj.tk/problem/4316
求一个仙人掌的最大独立集
先把他建出圆方树来,每个环选一个点当做“这个环的根”,作为对应方点的父亲,其他换上的点作为这个方点的儿子
考虑用 (f(u,1/2)) 来表示 (u) 的子树中,(u) 这个点选/不选的最大独立集大小
如何转移?圆点很好转移,(f(u,0)=sum max(f(v,1),f(v,0)),f(u,1)=1+sum f(v,0))
至于方点如何转移,要弄清它在此的实际意义,考虑一下这个方点的父亲(环的根)的转移情况
对于 (f(u,0)),它会为 (f(fa,1)) 产生贡献,那么就要选上环的根,则根旁边的两个点就都不能选。就是环的根以下的部分,不选和环的根相邻的两点,能选出的最大独立集大小
对于 (f(u,1)),它只有可能为 (f(fa,0)) 产生贡献,所以就不用管和不和环的根相连了。就是环的根一下的部分,能选出的最大独立集大小
至于如何求,在 DP()
函数里用正反两遍 dp 解决
其实这种仙人掌转圆方树的题似乎都是这个套路,就是转完圆方树以后,圆点的转移一般比较简单,而方点的可能还需要个 dp 啥的
比如这个题也是这样,只不过有些难我没写出来,dp 不好啥都玩蛋啊:https://www.luogu.com.cn/problem/P4244
#include<cstdio>
#include<algorithm>
#include<iostream>
#include<cmath>
#include<map>
#include<iomanip>
#include<cstring>
#define reg register
#define EN puts("")
inline int read(){
register int x=0;register int y=1;
register char c=std::getchar();
while(c<'0'||c>'9'){if(c=='-') y=0;c=std::getchar();}
while(c>='0'&&c<='9'){x=x*10+(c^48);c=std::getchar();}
return y?x:-x;
}
#define N 100006
#define M 300006
struct graph{
int fir[N],nex[M],to[M],tot;
inline void add(int u,int v){
to[++tot]=v;
nex[tot]=fir[u];fir[u]=tot;
}
}G,T;
int n,m,bcccnt;
int dfn[N],low[N],dfscnt;
int stack[N],top;
void tarjan(reg int u,int fa){
dfn[u]=low[u]=++dfscnt;stack[top++]=u;
for(reg int v,i=G.fir[u];i;i=G.nex[i]){
v=G.to[i];
if(v==fa) continue;
if(!dfn[v]){
tarjan(v,u);
low[u]=std::min(low[u],low[v]);
if(low[v]>dfn[u]) T.add(u,v);
}
else if(low[u]>dfn[v]){
low[u]=dfn[v];bcccnt++;
T.add(v,bcccnt);
for(reg int j=top-1;stack[j]^v;j--) T.add(bcccnt,stack[j]);
}
}
top--;
}
int tmp[N];
int f[N][2],dp[N][2];
inline void DP(reg int u){
tmp[0]=0;
for(reg int i=T.fir[u];i;i=T.nex[i]) tmp[++tmp[0]]=T.to[i];
if(tmp[0]==2){
int son1=tmp[1],son2=tmp[2];
f[u][0]=f[son1][0]+f[son2][0];//son1 son2 都不能选
f[u][1]=std::max(f[son1][0]+f[son2][0],std::max(f[son1][0]+f[son2][1],f[son1][1]+f[son2][0]));
return;
}
dp[1][0]=f[tmp[1]][0];dp[1][1]=0;//第一个不能选,所以是 0
for(reg int v,i=2;i<=tmp[0];i++){
v=tmp[i];
dp[i][0]=std::max(dp[i-1][0],dp[i-1][1])+f[v][0];
dp[i][1]=dp[i-1][0]+f[v][1];
}
f[u][0]=dp[tmp[0]][0];f[u][1]=dp[tmp[0]][0];
dp[tmp[0]+1][0]=dp[tmp[0]+1][1]=0;
for(reg int v,i=tmp[0];i;i--){
v=tmp[i];
dp[i][0]=std::max(dp[i+1][0],dp[i+1][1])+f[v][0];
dp[i][1]=dp[i+1][0]+f[v][1];
}
f[u][1]=std::max(f[u][1],std::max(dp[1][0],dp[1][1]));
}
void dfs(int u){
f[u][1]=1;
for(reg int v,i=T.fir[u];i;i=T.nex[i]){
v=T.to[i];
dfs(v);
if(u<=n){
f[u][0]+=std::max(f[v][1],f[v][0]);f[u][1]+=f[v][0];
}
}
if(u>n) DP(u);
}
inline void debug_tarjan(){
puts("finished tarjan");
printf("bcccnt=%d
",bcccnt);
for(reg int i=1;i<=bcccnt;i++){
printf("i=%d
",i);
for(reg int j=T.fir[i];j;j=T.nex[j]) printf("%d ",T.to[j]);
puts("");
}
}
int main(){
n=read();m=read();
for(reg int u,v,i=1;i<=m;i++){
u=read();v=read();
G.add(u,v);G.add(v,u);
}
bcccnt=n;
tarjan(1,1);
// debug_tarjan();
dfs(1);
printf("%d",std::max(f[1][0],f[1][1]));
return 0;
}