zoukankan      html  css  js  c++  java
  • CodeForces

    Arpa’s loud Owf and Mehrdad’s evil plan
    time limit per test 1 second
    memory limit per test 256 megabytes
    input standard input
    output standard output
    As you have noticed, there are lovely girls in Arpa’s land.

    People in Arpa’s land are numbered from 1 to n. Everyone has exactly one crush, i-th person’s crush is person with the number crushi.

    Someday Arpa shouted Owf loudly from the top of the palace and a funny game started in Arpa’s land. The rules are as follows.
    The game consists of rounds. Assume person x wants to start a round, he calls crushx and says: “Oww…wwf” (the letter w is repeated ttimes) and cuts off the phone immediately. If t > 1 then crushx calls crushcrushx and says: “Oww…wwf” (the letter w is repeated t - 1times) and cuts off the phone immediately. The round continues until some person receives an “Owf” (t = 1). This person is called the Joon-Joon of the round. There can’t be two rounds at the same time.
    Mehrdad has an evil plan to make the game more funny, he wants to find smallest t (t ≥ 1) such that for each person x, if x starts some round and y becomes the Joon-Joon of the round, then by starting from y, x would become the Joon-Joon of the round. Find such t for Mehrdad if it’s possible.
    Some strange fact in Arpa’s land is that someone can be himself’s crush (i.e. crushi = i).
    Input
    The first line of input contains integer n (1 ≤ n ≤ 100) — the number of people in Arpa’s land.
    The second line contains n integers, i-th of them is crushi (1 ≤ crushi ≤ n) — the number of i-th person’s crush.
    Output
    If there is no t satisfying the condition, print -1. Otherwise print such smallest t.
    Examples
    input
    4
    2 3 1 4
    output
    3
    input
    4
    4 4 4 4
    output
    -1
    input
    4
    2 1 4 3
    output
    1
    Note
    In the first sample suppose t = 3.
    If the first person starts some round:
    The first person calls the second person and says “Owwwf”, then the second person calls the third person and says “Owwf”, then the third person calls the first person and says “Owf”, so the first person becomes Joon-Joon of the round. So the condition is satisfied if x is 1.
    The process is similar for the second and the third person.
    If the fourth person starts some round:
    The fourth person calls himself and says “Owwwf”, then he calls himself again and says “Owwf”, then he calls himself for another time and says “Owf”, so the fourth person becomes Joon-Joon of the round. So the condition is satisfied when x is 4.
    In the last example if the first person starts a round, then the second person becomes the Joon-Joon, and vice versa.

    题意:有n个人打电话。第i个数表示第i个人,也就是每个人的下标是自己的名字,xi表示第i个人打给第几个人,这样类似链表的存储,每个人存储自己打给的下一个人是谁。然后如果符合,从x打给y,经过了t个人,再从y打给x经过了t个人,就认为这是一个环,找到最小的t为多大,输出这个t,注意,这个t在偶数人的环中,可以是从x出发,再回到x这段路程的一个中点,在奇数人数都会环中,因为不存在这个中点,所以从x到达y等于从y到达x的过程,就是走一遍环的过程,也就是说,x就是y。
    还有一点就是,这个t,所谓最小,是在若干个环中都能通用,在环中,可以无限走,可以从x直接到达y,也能从x经过一次y,再到达x,最后到达y,这样的多重循环,所以对于t来说,在每一个环都能从一个值跑到另一个值,或者跑回来,而不是某个环到达了目标位置,在另一个环中却走到一半。也就是说,这是所有环自己的t的最小公倍数。
    最后,如果有n个人中,某个人不在任何一个环,或一些人没有形成环,直接输出-1
    读了好多遍没读懂,自己试着复述了一下,也贴几个别人的版本供参考。

    题意:给出n个整数, 转换顺序为 x→a[x]→a[a[x]]→…. 一次规律继续进行下去t次,若终点为y,且满足y→a[y]→a[a[y]]→… 也进行t次 得到终点为x 。就称之为一个环。 要将数组里面分成若干个环,求最小的t为多
    大,不存在输出-1。
    题意:有n个人,第i个人可以打给第CRUSH(i)个人然后说”Owww…f”(w重复t次),然后CRUSH(i)个人打给CRUSH(CRUSH(i))说”Owww…f”(w重复t-1次),重复这个步骤直到t=1,这时候停在的第j个人称作Joon-Joon,现在问有没有最小的t使得任意x开始呼喊,最后y是Joon-Joon,然后y开始呼喊时,x是Joon-Joon,若有输出最小的t,否则输出-1。

    (转1)很多个人,一次给对应的人打电话。第t个人就是第一个人的Joon-Joon。问t最小为多少可以让每两个人
    互为Joon-Joon。其实是个数论题,对于每个人,找到其循环节是多少。然后求最小公倍数即可,注意可能会爆int,要
    用long long。

    (转2)题目扯了一大堆,主要是这个意思。n个人,每个人有一个打电话的对象(可以是自己),设t为打电话
    的总次数,当一个人打电话时,他只打给自己的对象,然后他的对象接着打给自己的对象。。。问最小的t,使得从任
    意的x开始打电话,总共打了t次后到达y,这一轮结束,然后y接着打,总共打了t次后又回到x,(x与y可等)。说白了,
    就是一个有向图,找遍所有的环,若环为偶数,则/2,求所有的最小公倍数。

    题解:首先明确,必须形成环才有解,必须每个人都在环中才有解,因此判断-1的情况。然后从第一个人开始遍历,找到下一个人,标记那个人,表示在环中。走完一个环后,找下一个未标记(不属于任何环)的人,继续找环,并计数环的个数以及每个环的长度(判断奇偶环)。最后,遍历记录的cnt个环,奇数环t为环长度,偶数环t为环长度的一半,不断对每个环t求最小公倍数。结果就是所有环的最小公倍数。

    #include<stdio.h>///题意,从某个人开始打电话,打给下一个人,数组的位置表示是第几个人,数组中存的数表示打给谁,类似链表一样的存储方式
    #include<string.h>///然后对于所有人,1~n,都有一个t值,表示,当打电话存在存在一个环时(最后能有人打给自己),t的最小值是多少。
    long long gcd(long long a,long long b)///如果打电话不存在环,或者有某人不在环中,成了一条直线,那就输出-1
    {
        while(b!=0)///t的意思是 从x->y经过了t人,再从y->x,也经过了t人,则是t值,因为这个t是对所有环通用的,所以他应该是所有环的长度的公倍数,又要求t的最小值,所以是最小公倍数
        {
            long long c=b;
            b=a%b;
            a=c;
        }
        return a;///因为t通用,所以t对于某个较大的环来说,可能是打出了一半的人,或者刚好打出了换的长度的人数,对于较小的环,则会循环打出几轮。
    }
    int main()
    {
        long long n,a[108],i;///步骤就是,先找出一个环,因为必定会存在环,否则不存在环就不用求t值,直接输出-1,所以先找出一个环,然后得到这个环的长度。
        bool vis[108];
        while(scanf("%lld",&n)!=EOF)
        {
            memset(vis,false,sizeof(vis));///用标记数组标记某个节点是否走过,如果走过就不用再从这个点遍历他在哪个环里了
            for(i=1;i<=n;i++)
            {
                scanf("%d",&a[i]);
            }
            bool flag=false;///是否存在非环的标记
            long long sum=1;///最后最小的t值的存储,因为要连续用于求最小公倍数,因此用long long 而且初值为1是不会影响结果
            for(i=1;i<=n;i++)///从第一个人开始检查环
            {
                if(vis[i])///如果遍历的那个人已经被标记,说明他属于别的环了,不用再从这个点开始检查环
                    continue;
                int x=a[i];
                vis[a[i]]=true;///开始的这个点打给的下一个人被标记,也就是说,要保证每一个人都接到了电话才算是一个环
                long long cnt=1;///环的长度计数,第一个人算是环的1个长度
                while(x!=i)///开始按着每个人存的下一个人的位置往下找人,直到找到的x回到的初始的i位置
                {
                    x=a[x];///传递
                    vis[x]=true;///标记
                    cnt++;///长度计数
                    if(cnt>n)///当长度大于总人数时,说明无限往下打电话,形成直线,不存在环,结束判断,直接-1
                    {
                        flag=true;
                        break;
                    }
                }
                if(flag)break;
                if(cnt%2==0)cnt/=2;///注意,这个t值是从x->y的长度,也是y->x的长度,如果是奇数长度环,那么x=y,t必为环长度,如果是偶数环,那么从这个环中间的那个人结束,再从他开始,就是t的值,所以要/2表示t
                sum=sum*cnt/gcd(cnt,sum);///最后得到一个环的长度就去求他们的最小公倍数,来一个求一个,求的是所有的环长度的最小公倍数
            }
            if(flag)
            {
                printf("-1
    ");
                continue;
            }
            else
            {
                printf("%lld
    ",sum);
            }
        }
    }
    
  • 相关阅读:
    设计模式-观察者模式
    获取ubuntu中软件包的有用地址
    vim 简单命令
    adb logcat 日志过滤
    shell编程——
    shell编程——参数传递
    Chromecast
    linux 广播
    【转】Git命令解说
    linux 多播
  • 原文地址:https://www.cnblogs.com/kuronekonano/p/11794340.html
Copyright © 2011-2022 走看看