[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带基环树的处理方法。这里有一个问题(By JhinLzh),问什么输出答案上面的for循环中的第一个dfs有用?明明在第二个dfs中所有的f和g都被更新了,为什么还要dfs?因为在第一个dfs中我们对f是强行负值,这样对于一些叶子节点来讲t值是没有更改的,这就导致f值在最后是一个极小值,这样的f是不会更新答案的。如果不写第一个dfs,使得一些在本不能更新答案的点更新了答案,导致答案错误。所以第一个dfs是必要的。

全部评论

相关推荐

05-11 11:48
河南大学 Java
程序员牛肉:我是26届的双非。目前有两段实习经历,大三上去的美团,现在来字节了,做的是国际电商的营销业务。希望我的经历对你有用。 1.好好做你的CSDN,最好是直接转微信公众号。因为这本质上是一个很好的展示自己技术热情的证据。我当时也是烂大街项目(网盘+鱼皮的一个项目)+零实习去面试美团,但是当时我的CSDN阅读量超百万,微信公众号阅读量40万。面试的时候面试官就告诉我说觉得我对技术挺有激情的。可以看看我主页的美团面试面经。 因此花点时间好好做这个知识分享,最好是单拉出来搞一个板块。各大公司都极其看中知识落地的能力。 可以看看我的简历对于博客的描述。这个帖子里面有:https://www.nowcoder.com/discuss/745348200596324352?sourceSSR=users 2.实习经历有一些东西删除了,目前看来你的产出其实很少。有些内容其实很扯淡,最好不要保留。有一些点你可能觉得很牛逼,但是面试官眼里是减分的。 你还能负责数据库表的设计?这个公司得垃圾成啥样子,才能让一个实习生介入数据库表的设计,不要写这种东西。 一个公司的财务审批系统应该是很稳定的吧?为什么你去了才有RBAC权限设计?那这个公司之前是怎么处理权限分离的?这些东西看着都有点扯淡了。 还有就是使用Redis实现轻量级的消息队列?那为什么这一块不使用专业的MQ呢?为什么要使用redis,这些一定要清楚, 就目前看来,其实你的这个实习技术还不错。不要太焦虑。就是有一些内容有点虚了。可以考虑从PR中再投一点产出
投递美团等公司10个岗位
点赞 评论 收藏
分享
鬼迹人途:你去投一投尚游游戏,服务器一面,第一个图算法,做完了给你一个策略题,你给出方案他就提出低概率问题,答不上当场给你挂
点赞 评论 收藏
分享
评论
点赞
收藏
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务