[bzoj3037/2068]创世纪[Poi2004]SZP_树形dp_并查集_基环树

Posted JZYshuraK_彧

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[bzoj3037/2068]创世纪[Poi2004]SZP_树形dp_并查集_基环树相关的知识,希望对你有一定的参考价值。

创世纪 SZP bzoj-3037/2068 Poi-2004

    题目大意:给你n个物品,每个物品可以且仅可以控制一个物品。问:选取一些物品,使得对于任意的一个被选取的物品来讲,都存在一个没有被选取的物品,而且选取的个数最大。

    注释:$1\le n \le 10^6$。

      想法:显然,和骑士类似的,是一个基环树森林。如果A物品可以控制B物品,那就有B物品向A物品连边。对于每一个基环树,如果这个基环树是树的话显然变成树形dp入门题,暴力树形dp即可。然后对于基环树来讲,我们依然记录环上两点,分别以这两点为根,然后特判树形dp即可。

    最后,附上丑陋的代码... ...

#include <cstdio>
#include <cstring>
#include <iostream>
#define N 1000010 
using namespace std;
int n,m,ans,now,tot;
int to[N],nxt[N],head[N],f[N],g[N],fa[N],ra[N],rb[N];
inline void add(int x,int y)
{
	to[++tot]=y;
	nxt[tot]=head[x];
	head[x]=tot;
}
int find(int x)
{
	return (fa[x]==x)?x:(fa[x]=find(fa[x]));
}
void dfs(int x)
{
	int t=1<<30;
	g[x]=0;
	for(int i=head[x];i;i=nxt[i])
	{
		if(to[i]!=now)
			dfs(to[i]);
		g[x]+=max(f[to[i]],g[to[i]]);
		t=min(t,max(f[to[i]],g[to[i]])-g[to[i]]);
	}
	f[x]=g[x]+1-t;
}
int main()
{
	scanf("%d",&n);
	int a;
	for(int i=1;i<=n;i++)
		fa[i]=i;
	for(int i=1;i<=n;i++)
	{
		scanf("%d",&a);
		if(find(a)!=find(i))
		{
			add(a,i);
			fa[fa[a]]=fa[i];
		}
		else
			ra[++m]=a,rb[m]=i;
	}
	for(int i=1;i<=m;i++)
	{
		dfs(ra[i]),now=ra[i];
		dfs(rb[i]),a=f[rb[i]];
		f[ra[i]]=g[ra[i]]+1;
		dfs(rb[i]),ans+=max(a,g[rb[i]]);
	}
	printf("%d",ans);
	return 0;
}

 

    小结:基环树dp是一种常见的,树形dp带基环树的处理方法。

 

以上是关于[bzoj3037/2068]创世纪[Poi2004]SZP_树形dp_并查集_基环树的主要内容,如果未能解决你的问题,请参考以下文章

BZOJ1135[POI2009]Lyz 线段树

BZOJ2085[Poi2010]Hamsters hash+倍增floyd

bzoj1531[POI2005]Bank notes 多重背包dp

bzoj2085[Poi2010]Hamsters Hash+倍增Floyd

[BZOJ2788][Poi2012]Festival

bzoj1137POI2009Wsp 岛屿