3929. 【NOIP2014模拟11.6】创世纪 (Standard IO)
Time Limits: 1000 ms Memory Limits: 65536 KB
Description
上帝手中有着n种被称作“世界元素”的东西,现在他要把它们中的一部分投放到一个新的空间中去以建造世界。每种世界元素都可以限制另外一种世界元素,所以说上帝希望所有被投放的世界元素都有至少一个没有被投放的世界元素能够限制它,这样上帝就可以保持对世界的控制。 由于那个著名的有关于上帝能不能制造一块连自己都不能举起的大石头的二律背反命题,我们知道上帝不是万能的,而且不但不是万能的,他甚至有事情需要找你帮忙——上帝希望知道他最多可以投放多少种世界元素,但是他只会O(2^n)级别的算法。虽然上帝拥有无限多的时间,但是他也是个急性子。你需要帮助上帝解决这个问题。
第一行一个正整数n,表示世界元素的数目。 第二行n个正整数a_1, a_2, …, a_n。a_i表示第i个世界元素能够限制的世界元素的编号。
Output
最多可以投放的世界元素的数目。
6 2 3 1 3 6 5
Sample Output
3 样例说明: 选择2、3、5 三个世界元素即可,分别有1、4、6来限制它们。
Data Constraint
30%的数据,n<=10。 60%的数据,n<=10^5。 100%的数据,a_i<=n<=10^6。
题解
贪心或dp可以过(但是dp方程我没想到)
首先看题,把限制点指向被限制点,就形成了环套树,而且只有进环没有出环
很明显,没有入度的点是一定不能选的,只能用来限制
开始贪心 1. 对于树,从入度为0的点开始推进,隔一个选一个 2. 对于环,统计环内节点数,取一半就可以了
代码
#include<cstdio>
#define N
1000010
long
next[N],r[N];
bool b[N];
int main()
{ long n,i,j,ans=
0,num;
scanf(
"%ld",&n);
for(i=
1;i<=n;i++){
scanf(
"%ld",&
next[i]);
r[
next[i]]++;
}
for(i=
1;i<=n;i++)
if(!r[i]&&!b[i]){
b[i]=
true;
if(!b[
next[i]]){
b[
next[i]]=
true;
r[
next[i]]
r[
next[
next[i]]]
ans++;
for(j=
next[
next[i]];!r[j]&&!b[j];j=
next[
next[j]]){
b[j]=
true;
if(!b[
next[j]]){
b[
next[j]]=
true;
r[
next[j]]
r[
next[
next[j]]]
ans++;
}
else break;
}
}
}
for(i=
1;i<=n;i++)
if(r[i]&&!b[i]){
num=
1;
b[i]=
true;
for(j=
next[i];j!=i;j=
next[j]){
b[j]=
true;
num++;
}
ans+=num/
2;
}
printf(
"%ld\n",ans);
return 0;
}