BZOJ 4316 小C的独立集

题目链接

题解

在树上做是一个经典的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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值