码迷,mamicode.com
首页 > 其他好文 > 详细

bzoj4316 小C的独立集

时间:2020-11-11 16:24:36      阅读:8      评论:0      收藏:0      [点我收藏+]

标签:表示   产生   函数   char   tps   bzoj   需要   bug   tin   

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\n",bcccnt);
	for(reg int i=1;i<=bcccnt;i++){
		printf("i=%d\n",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;
}

bzoj4316 小C的独立集

标签:表示   产生   函数   char   tps   bzoj   需要   bug   tin   

原文地址:https://www.cnblogs.com/suxxsfe/p/13766086.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!