zoukankan      html  css  js  c++  java
  • 【BZOJ3037/2068】创世纪/[Poi2004]SZP 树形DP

    【BZOJ3037】创世纪

    Description

    applepi手里有一本书《创世纪》,里面记录了这样一个故事……
    上帝手中有着N 种被称作“世界元素”的东西,现在他要把它们中的一部分投放到一个新的空间中去以建造世界。每种世界元素都可以限制另外一种世界元素,所以说上帝希望所有被投放的世界元素都有至少一个没有被投放的世界元素能够限制它,这样上帝就可以保持对世界的控制。
    由于那个著名的有关于上帝能不能制造一块连自己都不能举起的大石头的二律背反命题,我们知道上帝不是万能的,而且不但不是万能的,他甚至有事情需要找你帮忙——上帝希望知道他最多可以投放多少种世界元素,但是他只会O(2^N) 级别的算法。虽然上帝拥有无限多的时间,但是他也是个急性子。你需要帮助上帝解决这个问题。

    Input

    第一行是一个整数N,表示世界元素的数目。
    第二行有 N 个整数A1, A2, …, AN。Ai 表示第i 个世界元素能够限制的世界元素的编号。

    Output

    一个整数,表示最多可以投放的世界元素的数目。

    Sample Input

    6
    2 3 1 3 6 5

    Sample Output

    3

    HINT

    样例说明
    选择2、3、5 三个世界元素即可。分别有1、4、6 来限制它们。

    数据范围与约定
    对于30% 的数据,N≤10。
    对于60% 的数据, N≤10^5。
    对于 100% 的数据,N≤10^6,1≤Ai≤N,Ai≠i。

    题解:基环树的DP

    因为每个元素只能限制一个别的元素,而一个元素可以被许多个元素限制,我们就被限制点作为限制点的父亲(也就是说一个点被它所有儿子限制)

    先假设没有环,f[i]表示选择i,g[i]表示不选择i,此时无脑DP

    然后如果出现了环该怎么办?

    假设在加入边 u->v(v能控制u)时出现了环,说明u一定在v的子树里,于是先DFS以u为根的子树,然后分两种情况

    1.选择v,那么直接DFS以v为根的子树,用f[v]更新答案

    2.不选v,那么u可以免费选择,即f[i]=g[i]+1,然后在DFS以v为根的子树,用g[v]更新答案

    注意DFS(v)的时候不要进入以u为根的子树

     同BZ2068

    #include <cstdio>
    #include <cstring>
    #include <iostream>
    using namespace std;
    const int maxn=1000010;
    int n,m,ans,now,cnt;
    int to[maxn],next[maxn],head[maxn],f[maxn],g[maxn],fa[maxn],ra[maxn],rb[maxn];
    int find(int x)
    {
    	return (fa[x]==x)?x:(fa[x]=find(fa[x]));
    }
    void dfs(int x)
    {
    	int i,t=1<<30;
    	g[x]=0;
    	for(i=head[x];i!=-1;i=next[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;
    }
    void add(int a,int b)
    {
    	to[cnt]=b;
    	next[cnt]=head[a];
    	head[a]=cnt++;
    }
    int main()
    {
    	scanf("%d",&n);
    	int i,a;
    	memset(head,-1,sizeof(head));
    	for(i=1;i<=n;i++)	fa[i]=i;
    	for(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(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;
    }
  • 相关阅读:
    一个简单的C++程序反汇编解析
    一个简单的C++程序反汇编解析
    Oracle 10g 用户数及价格
    若何应对被公开的Oracle口令加密算法(3)
    Solaris办事管理对象 SMF快速入门指南(1)
    Solaris 10完成安好Kerberos身份验证(1)
    Solaris效力操持工具 SMF疾速入门指南(3)
    Oracle Warehouse Builder 自动化ETL措置历程(4)
    如何经过PHP获得MySQL procedure成效
    Solaris 10下设置EJB 3.0情况(2)
  • 原文地址:https://www.cnblogs.com/CQzhangyu/p/6534765.html
Copyright © 2011-2022 走看看