一群男孩与女孩(共N人)打破了妈妈最喜爱的杯子。妈妈很生气并把孩子们用正整数从1到N编号。然后她走近1号孩子,问道:"谁打破了杯子?" "我" 1号孩子回答道,并受到了惩罚.
当然,你知道这样的情节是非常理想的。但实际上那个1号孩子也许会说: "这不是我干的!这是第K1号孩子干的!" (我们不知道这是真话还是谎言)。然后,妈妈走近第2号孩子并问了同样的问题……
一些孩子尝试说出真相,其他孩子按顺序说出一些情况。但有些孩子不同意把做错事的孩子泄露给妈妈:每个孩子都指出杯子是被另一个孩子砸破的——并行成一个循环。为此,妈妈非常苦恼。她绞尽脑汁地去回忆每一个孩子所告诉她的关于杯子的事情,并把每个孩子给她的证言写在一张纸上。现在,她马上着手调查这件事。首先,她决定去找出一些孩子之前所描述的“集体证言”。你要编写一个程序帮妈妈完成这件事。
input:
第一行包含一个正整数T (0 < T < 1001)为测试数据的总数目。每个测试数据由两行组成:第一行包含一个正整数 N (0 < N < 25001)为孩子的数目。第二行包含N 个由空格隔开的数,那是每一个孩子的证言。第I个数表示第I个孩子所指出打破杯子的那个孩子的编号。而0则表示这第I个孩子突然承认了错误。
output:
你应该用一行来回答一个测试点。它包括"YES", 如果这个孩子事件的结果看起来是不容辩驳的: 一个孩子承认他自己打破了那个杯子, 并且没有每个孩子都指出杯子是被另一个孩子砸破的——并行成一个循环这种情况.否则你就输出"NO"。
input:
4
4
2 0 2 2
4
2 0 2 1
5
2 3 4 1 3
3
0 3 2
output:
YES
YES
NO
NO
分析:此题本人用的是并查集:以第i个人说的那个人作为边插入集合中,一旦有一个人说的话形成了环(即在已有的集合一面),那么一定是NO,还有当有多个人承认时也是NO,其它的就是YES。
【参考程序】:
#include<iostream>
using namespace std;
int n,lie,test;
int f[25001];
int Find(int x)
{
if (x!=f[x]) f[x]=Find(f[x]);
return f[x];
}
void Union(int x,int y)
{
int a=Find(x),b=Find(y);
if (a!=b) f[b]=a;
}
void make()
{
int x;
for (int i=1;i<=n;i++)
{
scanf("%d",&x);
if (x==0)
{
lie++;continue;
}
if (Find(i)==Find(x))
{
for (int j=i+1;j<=n;j++) scanf("%d",&x);
printf("NO\n");
return ;
}
Union(i,x);
}
if (lie!=1) printf("NO\n");
else printf("YES\n");
}
int main()
{
scanf("%d",&test);
for (int t=1;t<=test;t++)
{
scanf("%d",&n);
for (int i=1;i<=n;i++) f[i]=i;
lie=0;
make();
}
return 0;
}