题解 P4089 【[USACO17DEC]The Bovine Shuffle】
首先,我们理解一下题意
每一个位置上都有一个牛。每头牛都会变换
给你n个数,分别是a1,a2……an。ai表示每一次变换位置为i的牛会变到位置为ai的地方。变换后,有的位置上有多个牛,那么他们下一次变换,这个位置上的所有牛都会这么变。
问无论几次变化后,一共有多少个位置上有牛
看看样例:
in:
4
3 2 1 3
out:
3
画个图,理解一下
欸?这不就是个有向图吗?
我们来画一画:
结合样例,我们来看看
输出了3,是哪3个?是1,2,3三个位置。那么为什么4号没有牛呢?我们可以发现因为1,2,3入度都大于0,唯独4小于0.哦?难道和入度有关?
猜想
我们不妨有一个猜想:如果有一个点,他的收入可以维持他的支出,那么这个点上一直有牛。也就是
if(入度-出度>=0){
ans++;
}
这就对了吗?我们欣喜的发现样例过了。提交一下——WA
这是为什么呢?来看看这组数据——
in:
1 1 2
输出应该是1,可是以我们这样判断就是2
QAQ泪奔
猜想2
我们可以发现,导致这种错误的原因是第一轮3给了2一头牛,可是第二轮3没有牛了,从而让2后面每轮得到的牛为0。就是因为3入度为0,所以"养不起"2
所以我们可以猜想:如果一个点入度为0,那么这个点指向的点入度--。(大家听起来应该很费劲,用伪代码给大家理解)
if(in[f[i]]==0)
{
in[i]--;
{
f[i]存储的是指向自己的点
可是f[i]里可能有多个点,怎么存呢?
我们用一个队列存储入度为0的点,然后一个循环减去,最后找到入度不为0的就好了。
这不就是拓扑吗?
实现
#include<bits/stdc++.h>
using namespace std;
int n,a[100005],ans;
int out[100005],in[100005];
queue<int> q;
int main()
{
cin>>n;
for(int i=1;i<=n;i++)
{
cin>>a[i];
in[a[i]]++;
}
for(int i=1;i<=n;i++)
{
if(in[i]==0){
q.push(i);
}
}
while(!q.empty())
{
int tmp=q.front();
q.pop();
in[a[tmp]]--;
if(in[a[tmp]]==0)
{
q.push(a[tmp]);
}
}
for(int i=1;i<=n;i++)
{
if(in[i]!=0)
{
ans++;
}
}
cout<<ans;
return 0;
}