题解
在树上做是一个经典的DP,记
f
[
i
]
[
0
/
1
]
f[i][0/1]
f[i][0/1]为u子树内u是否选的最大独立集,转移显然。在仙人掌上,还是先像树那样dfs,考虑每一条返祖边的影响,即对于每一个环在做一个dp,多记一维代表末尾是否选即可。注意dp是顺序,应保证每个点除了有伸向祖先的返祖边的子树外都考虑过,最后考虑有伸向祖先的返祖边的子树即可。
代码:
#include<bits/stdc++.h>
using namespace std;
const int N=2e5+5;
int n,m,hd[N],to[N],nx[N],tt=1;
void add(int u,int v){
nx[++tt]=hd[u]; to[hd[u]=tt]=v;
}
int fa[N],dp[N],mn[N];
void pre(int u,int fl){
for(int e=hd[u];e;e=nx[e]) if(e!=fl && (e^1)!=fl){
int v=to[e];
if(dp[v]){
mn[u]=min(mn[u],dp[v]);
continue;
}
dp[v]=mn[v]=dp[u]+1; fa[v]=u;
pre(v,e); mn[u]=min(mn[u],mn[v]);
}
}
int f[N][2],g[N][2][2];
void dfs(int u,int fl){
int w=0,sn=0,sf=0; f[u][0]=0; f[u][1]=1;
for(int e=hd[u];e;e=nx[e]) if(e!=fl && (e^1)!=fl){
int v=to[e];
if(dp[v]!=dp[u]+1){
if(dp[v]<dp[u]) w=v;
continue;
}
if(mn[v]<dp[u]){
sn=v,sf=e; continue;
}
dfs(v,e);
if(mn[v]>dp[u]){
f[u][0]+=max(f[v][0],f[v][1]);
f[u][1]+=f[v][0];
}
}
if(sn) dfs(sn,sf);
if(w){
for(int p=0;p<2;p++) g[u][p][p]=f[u][p],g[u][p][!p]=-N;
int y=u,x=fa[u];
while(1){
for(int p=0;p<2;p++)
g[x][0][p]=f[x][0]+max(g[y][0][p],g[y][1][p]),
g[x][1][p]=f[x][1]+g[y][0][p];
if(x==w){
f[x][0]=max(g[x][0][0],g[x][0][1]);
f[x][1]=g[x][1][0];
break;
}
y=x; x=fa[x];
}
}
}
int main()
{
cin>>n>>m;
for(int u,v,i=1;i<=m;i++)
scanf("%d%d",&u,&v),add(u,v),add(v,u);
dp[1]=mn[1]=1; pre(1,0);
dfs(1,0);
cout<<max(f[1][0],f[1][1])<<endl;
return 0;
}