Bzoj_4316 小C的独立集

题意

给定一个仙人掌,求出这个仙人掌的最大独立点集。

\(n \leqslant 5e4,m \leqslant 6e4\)

题解

哈哈,第一次写仙人掌DP,大脑爆炸。
这里用一种直接DP的方式。设\(f_{x,i,j}\)表示点x的选择情况为i,点x到父亲的那条边所在的环中,深度最大(位于底部)的点的选择情况为j的最大方案。转移嘛,自己想想吧。详见代码。

#include<cmath>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
const int maxn=5e4;
int n,m,tot,ans;
int pre[maxn*4+8],now[maxn+8],son[maxn*4+8];
int fa[maxn+8],color[maxn+8],f[maxn+8][2][2],dep[maxn+8];

int read()
{
	int x=0,f=1;char ch=getchar();
	for (;ch<'0'||ch>'9';ch=getchar()) if (ch=='-') f=-1;
	for (;ch>='0'&&ch<='9';ch=getchar()) x=x*10+ch-'0';
	return x*f;
}

void add(int u,int v)
{
	pre[++tot]=now[u];
	now[u]=tot;
	son[tot]=v;
}

void dfs(int x)
{
	dep[x]=dep[fa[x]]+1;
	for (int p=now[x];p;p=pre[p])
		{
			int child=son[p];
			if (child==fa[x]) continue;
			if (!dep[child])
				{
					fa[child]=x,dfs(child);
					for (int i=0;i<2;i++)
							for (int j=0;j<2;j++)
								{
									int res=0;
									for (int k=0;k<2;k++)
										for (int l=0;l<2;l++)
											{
												if (i&k) continue;
												if (color[child]!=2&&j!=l) continue;
												if (color[child]==1&&k!=l) continue;
												if (color[child]==2&&(i&l)) continue;
												res=max(res,f[child][k][l]);
											}
									f[x][i][j]+=res;
								}
				}	
			else
				if (dep[x]>dep[child])
					{
						color[x]=1;
						int res=x;
						while(fa[res]!=child) res=fa[res];
						color[res]=2;
					}
		}
	f[x][1][0]++,f[x][1][1]++;
	if (color[x]==1)
		{
			f[x][0][1]=f[x][0][0]=max(f[x][0][1],f[x][0][0]);
			f[x][1][1]=f[x][1][0]=max(f[x][1][0],f[x][1][1]);
			f[x][1][0]=f[x][0][1]=0;
		}
}

int main()
{
	n=read(),m=read();
	for (int i=1;i<=m;i++)
		{
			int u=read(),v=read();
			add(u,v),add(v,u);
		}
	for (int i=1;i<=n;i++)
		if (!dep[i])
			{
				dfs(i);
				int res=0;
				for (int j=0;j<2;j++)
					for (int k=0;k<2;k++)
						res=max(res,f[i][j][k]);
				ans+=res;
			}
	printf("%d\n",ans);
	return 0;
}
posted @ 2019-01-15 21:23  Alseo_Roplyer  阅读(129)  评论(0编辑  收藏  举报