https://www.luogu.org/problemnew/show/P2661

C++版本一

并查集

/*
*@Author:   STZG
*@Language: C++
*/
#include <bits/stdc++.h>
#include<iostream>
#include<algorithm>
#include<cstdlib>
#include<cstring>
#include<cstdio>
#include<string>
#include<vector>
#include<bitset>
#include<queue>
#include<deque>
#include<stack>
#include<cmath>
#include<list>
#include<map>
#include<set>
//#define DEBUG
#define RI register int
using namespace std;
typedef long long ll;
//typedef __int128 lll;
const int N=200000+10;
const int M=100000+10;
const int MOD=1e9+7;
const double PI = acos(-1.0);
const double EXP = 1E-8;
const int INF = 0x3f3f3f3f;
int t,n,m,k,q;
int ans,cnt,flag,temp,sum;
int pre[N];
int dist[N];
vector<int>G[N];
char str;
struct node{};
int find(int x){
    if(pre[x]!=x){
        int last=pre[x];
        //cout<<x<<" "<<pre[x]<<endl;
        pre[x]=find(pre[x]);
        dist[x]+=dist[last];
    }
    return pre[x];
}
void check(int a,int b){
    int tx=find(a);
    int ty=find(b);
    if(tx!=ty){
        pre[tx]=ty;
        dist[a]=dist[b]+1;
    }else{
        ans=min(ans,dist[a]+dist[b]+1);
    }
}
int main()
{
#ifdef DEBUG
	freopen("input.in", "r", stdin);
	//freopen("output.out", "w", stdout);
#endif
    //ios::sync_with_stdio(false);
    //cin.tie(0);
    //cout.tie(0);
    //scanf("%d",&t);
    //while(t--){
    scanf("%d",&n);
    ans=INF;
    for(int i=1;i<=n;i++){
        pre[i]=i;
    }
    for(int i=1;i<=n;i++){
        scanf("%d",&m);
        check(i,m);
    }
    cout<<ans<<endl;

    //}

#ifdef DEBUG
	printf("Time cost : %lf s\n",(double)clock()/CLOCKS_PER_SEC);
#endif
    //cout << "Hello world!" << endl;
    return 0;
}

C++版本二

题解:将每个同学当作一个结点,将每次传递当作一条有向的线,一轮游戏便可以看作是个图。

那么,不难发现,如果想要有一个同学说出的生日重新传回自己的耳中,图中必定有一个环(环中的每个结点互相可以连通)。

如果有一个同学无法从别人口中传到自己的生日,也能说明是在游戏中他的生日落入了一个循环,依旧是一个环,只不过环的结点不包括那个人罢了。既然要求最小的游戏轮数,也就是要求最小的环,楼上对于环的解析很清楚了。

我的思路是,从每个人开始,按照输入的信息,寻找环,如果有个环没有被找到,就打个标记(那个人的编号),如果环已经被打标记了,就不用多浪费时间复杂度了。然后,用得到的环的边数更新 x 最小值。预计时间复杂度为 O(n)。

/*
*@Author:   STZG
*@Language: C++
*/
#include <bits/stdc++.h>
#include<iostream>
#include<algorithm>
#include<cstdlib>
#include<cstring>
#include<cstdio>
#include<string>
#include<vector>
#include<bitset>
#include<queue>
#include<deque>
#include<stack>
#include<cmath>
#include<list>
#include<map>
#include<set>
//#define DEBUG
#define RI register int
using namespace std;
typedef long long ll;
//typedef __int128 lll;
const int N=200000+10;
const int M=100000+10;
const int MOD=1e9+7;
const double PI = acos(-1.0);
const double EXP = 1E-8;
const int INF = 0x3f3f3f3f;
int t,n,m,k,q;
int ans,cnt,flag,temp,sum;
int a[N];
int dist[N];
int dp[N];
vector<int>G[N];
char str;
int main()
{
#ifdef DEBUG
	freopen("input.in", "r", stdin);
	//freopen("output.out", "w", stdout);
#endif
    //ios::sync_with_stdio(false);
    //cin.tie(0);
    //cout.tie(0);
    //scanf("%d",&t);
    //while(t--){
    scanf("%d",&n);
    ans=INF;
    for(int i=1;i<=n;i++){
        scanf("%d",&a[i]);
    }
    for(int i=1;i<=n;i++){
        dist[i]=1;
        int t=i;
        int p=a[i];
        while(dp[p]!=i){
            if(dp[p]&&dp[p]<i)
                break;
            dist[p]=dist[t]+1;
            dp[t]=i;
            t=p;
            p=a[t];
        }
        if(dp[p]==i&&ans>dist[t]-dist[p]+1)
            ans=dist[t]-dist[p]+1;
    }
    cout<<ans<<endl;
    //}

#ifdef DEBUG
	printf("Time cost : %lf s\n",(double)clock()/CLOCKS_PER_SEC);
#endif
    //cout << "Hello world!" << endl;
    return 0;
}