bzoj2037 创世纪

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。

和1040基本一样
题意求有向图中的最小支配集(单向支配)
做法
找环拆边:
对于一条边u,v。如果选u那么v一开始就被支配所以g[v]=0;
如果不选u,那么就正常的做一遍最小匹配
取最大值
f[i]代表选这个点时以I为根的子树的最小支配,g[i]代表这个点不选也被支配以I为根的子树时最小支配

#include<cstdio>
#include<cstring>
#include<utility>
#include<algorithm>
#include<iostream>
#include<queue>
#include<stack>
#include<cmath>
#include<cstdlib>
#include<ctime>
#define INF 0x3f3f3f
using namespace std;
inline int read()
{
    char ch='*';
    int f=1;
    while(!isdigit(ch=getchar())) if(ch=='-') f=-1;
    int num=ch-'0';
    while(isdigit(ch=getchar())) num=num*10+ch-'0';
    return num*f;
}
typedef long long ll;
const int maxn = 2000005;
struct edge{
    int next,to;
    bool ban;
}e[maxn<<1];
int cnt,h[maxn],v[maxn];
int g[maxn],f[maxn],ans;
int a[maxn],n,m,U,V,E;
int p;
int control;
inline void add(int from,int to)
{
    e[++cnt].next=h[from];
    e[cnt].to=to;
    h[from]=cnt;
}

void dfs(int x)
{
    v[x]=1;
    if(v[a[x]]) {
        p=x;
        return ;
    }
    dfs(a[x]);
}
void dp(int x,int fa,int ban)
{
    f[x]=1;
    g[x]=INF;
    v[x]=1;
    if(x==control) g[x]=0;
    for(register int i=h[x];i;i=e[i].next)
    {
        if(i==ban||e[i].to==fa) continue;
    //  if((i^1)==from) continue ;
        dp(e[i].to,x,ban);
        g[x]+=min(f[e[i].to],g[e[i].to]);
        g[x]=min(g[x],f[x]+f[e[i].to]-1);
        f[x]+=min(g[e[i].to],f[e[i].to]);
    //  g[x]+=f[e[i].to];
    }
}       
int main()
{
    n=read();
    int u,vv;
    for(register int i=1;i<=n;i++)
    {
        a[i]=read();
        //add(u,i);
        add(a[i],i);
    }
    for(register int i=1;i<=n;i++)
    {
        if(!v[i])       
        {
            dfs(i);
            //printf("%d %d %d \n",U,V,E);          
            e[p].ban=1;
            control=a[p];
            dp(p,0,p);
            control=0;
            int temp=f[p];
            dp(p,0,p);
            ans+=min(temp,g[p]);
        }
    }
    cout<<n-ans<<endl;
    return 0;
}














  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值