• bzoj4316 小C的独立集


    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;
    }
    
  • 相关阅读:
    Windows 科研软件推荐
    有关Python 包 (package) 的基本知识
    《Using Python to Access Web Data》Week4 Programs that Surf the Web 课堂笔记
    Coursera助学金申请模板
    《Using Databases with Python》 Week2 Basic Structured Query Language 课堂笔记
    Jupyter 解决单个变量输出问题
    解决 pandas 中打印 DataFrame 行列显示不全的问题
    《Using Python to Access Web Data》 Week3 Networks and Sockets 课堂笔记
    缓存击穿及解决方案
    jvm垃圾收集器
  • 原文地址:https://www.cnblogs.com/suxxsfe/p/13766086.html
Copyright © 2020-2023  润新知