2019.03.09【BZOJ4316】小C的独立集(仙人掌)(树形DP)

版权声明:转载请声明出处,谢谢配合。 https://blog.csdn.net/zxyoi_dreamer/article/details/88364351

传送门


解析:

仙人掌DP水题。。。

考虑一般的树是怎么做的DP。

f [ u ] [ 1 / 0 ] f[u][1/0] 表示选或不选 u u 的时候, u u 的子树中的最大独立集。

对于仙人掌来说,我们不能这样做了,因为非树边会导致我们不知道环顶和环底的限制之间会不会产生矛盾。

但是我们发现各个环是相对独立的。

所以我们可以对于每个环,先处理出除环顶外的每个点的外向子图中的最大独立集大小。

将环拿出来,断掉环顶和环底之间的边。

直接分强制选择链顶和强制不选择链顶做一次DP就行了。


代码:

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define gc get_char
#define re register
#define cs const

namespace IO{
	inline char get_char(){
		static cs int Rlen=1<<20|1;
		static char buf[Rlen],*p1,*p2;
		return (p1==p2)&&(p2=(p1=buf)+fread(buf,1,Rlen,stdin),p1==p2)?EOF:*p1++;
	}
	
	inline int getint(){
		re char c;
		while(!isdigit(c=gc()));re int num=c^48;
		while(isdigit(c=gc()))num=(num+(num<<2)<<1)+(c^48);
		return num;
	}
}
using namespace IO;

cs int N=5e4+4;
vector<int> edge[N];
inline void addedge(int u,int v){
	edge[u].push_back(v);
	edge[v].push_back(u);
}

int dfn[N],low[N],dfs_clock,fa[N];
int f[N][2];

inline void loop_dp(int u,int v){
	int t0,t1,f0=0,f1=0;
	for(int re x=v;x^u;x=fa[x]){
		t0=f0+f[x][0],t1=f1+f[x][1];
		f0=max(t0,t1),f1=t0;
	}
	f[u][0]+=f0;
	f0=0,f1=-1e9;
	for(int re x=v;x^u;x=fa[x]){
		t0=f0+f[x][0],t1=f1+f[x][1];
		f0=max(t0,t1),f1=t0;
	}
	f[u][1]+=f1;
}

inline void tarjan(int u,int pa){
	fa[u]=pa;
	dfn[u]=low[u]=++dfs_clock;
	f[u][1]=1,f[u][0]=0;
	for(int re e=edge[u].size()-1,v;~e;--e)
	if((v=edge[u][e])^pa){
		if(!dfn[v]){
			tarjan(v,u);
			low[u]=min(low[u],low[v]);
			if(low[v]>dfn[u]){
				f[u][1]+=f[v][0];
				f[u][0]+=max(f[v][0],f[v][1]);
			}
		}
		else low[u]=min(low[u],dfn[v]);
	}
	for(int re e=edge[u].size()-1,v;~e;--e)
	if(fa[v=edge[u][e]]!=u&&dfn[v]>dfn[u])loop_dp(u,v);
}

int n,m;
int ans;
signed main(){
	n=getint(),m=getint();
	for(int re i=1;i<=m;++i)addedge(getint(),getint());
	for(int re i=1;i<=n;++i)if(!dfn[i])tarjan(i,0),ans+=max(f[i][0],f[i][1]);
	cout<<ans<<"\n";
	return 0;
}

猜你喜欢

转载自blog.csdn.net/zxyoi_dreamer/article/details/88364351