- 【NOIP2014模拟11.6】创世纪
Description
上帝手中有着n种被称作“世界元素”的东西,现在他要把它们中的一部分投放到一个新的空间中去以建造世界。每种世界元素都可以限制另外一种世界元素,所以说上帝希望所有被投放的世界元素都有至少一个没有被投放的世界元素能够限制它,这样上帝就可以保持对世界的控制。
由于那个著名的有关于上帝能不能制造一块连自己都不能举起的大石头的二律背反命题,我们知道上帝不是万能的,而且不但不是万能的,他甚至有事情需要找你帮忙——上帝希望知道他最多可以投放多少种世界元素,但是他只会O(2^n)级别的算法。虽然上帝拥有无限多的时间,但是他也是个急性子。你需要帮助上帝解决这个问题。
Input
第一行一个正整数n,表示世界元素的数目。
第二行n个正整数a_1, a_2, …, a_n。a_i表示第i个世界元素能够限制的世界元素的编号。
Output
最多可以投放的世界元素的数目。
分析:找出所有没有入度的点,那么这些点必定不能选入集合中,我们沿着这些点上溯,将可以选进集合的点都选进集合。这里“可以选进”的定义为存在一个没有被选进集合的前驱。于是环上就会有若干个由树上决定它要选进集合的点。我们将环按照已选入集合的点来分裂成若干段,每一段按照原来的贪心方法来贪心即可。
代码
#include <cstdio>
#include <queue>
#define maxn 2000000
using namespace std;
int a[maxn],d[maxn],n,ans;
bool v[maxn],v1[maxn];
queue<int> q;
void bfs()
{
for (int i=1;i<=n;i++)
if (d[i]==0) q.push(i);
while (!q.empty())
{
int x=q.front();
q.pop();
v[x]=true;
if (!v[a[x]])
{
ans++;
v[a[x]]=true;
d[a[a[x]]]--;
if (d[a[a[x]]]==0&&!v[a[a[x]]])
q.push(a[a[x]]);
}
}
}
int main()
{
scanf("%d",&n);
for (int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
d[a[i]]++;
}
bfs();
for (int i=1;i<=n;i++)
if (!v[i]&&!v1[i])
{
int j=i;
int p=0;
while (!v1[a[j]])
{
j=a[j];
v1[j]=true;
p++;
}
ans+=p/2;
}
printf("%d",ans);
}