ACM模版

描述

题解

初始给定一个 1n 1 ∼ n 的全排列,经过 3n 3 n 次随机交换或者 7n+1 7 n + 1 次随机交换,得到一个新的全排列,问这个全排列是通过 3n 3 n 次随机交换还是 7n+1 7 n + 1 次随机交换形成的?

这个题很简单,放在 E E 有些坑了……昨天晚上连题都没有来得及看。

首先,我们分析,两个数经过 2 x + 1 交换相当于经过 1 1 次交换,经过 2 x 次交换,相当于没有交换,如果不止两个数来回交换,而是多个数之间来回交换,那么可以看成一个环,道理是一样的。所以不管是哪种交换,里面一定存在很多没有实际效果的交换,也就是 2x 2 x 次交换。

这里我们可以假设是通过 3n 3 n 次操作获得的全排列,那么 3n=2x+cnt 3 n = 2 x + c n t 2x 2 x 具体是多少我们无法直接求出来,因为它是无实际效果的,但是我们可以求出来 cnt c n t cnt c n t 表示有效的操作次数,经过有效的操作次数我们得到了这个全排列,那么我们同样可以通过 cnt c n t 次操作来恢复到初始化的 1n 1 ∼ n 有序的全排列,如此这般我们就可以求出 cnt c n t ,得到 y=3ncnt y = 3 n − c n t ,此时我们判断一下,如果 y=2x y = 2 x ,那么说明我们先前的假设是对的,反之说明这个全排列是经过 7n+1 7 n + 1 次操作获得的(代码 One)。

在查看别人的代码时,发现了一种很莫名其妙的解法(代码 Two),实在是参悟不透,也发一下,如果大佬们有何高见,请指点。

代码

One:

#include <cstdio>

const int MAXN = 1e6 + 10;

int n;
int a[MAXN], p[MAXN];

int main()
{
    scanf("%d", &n);
    for (int i = 1; i <= n; i++)
    {
        scanf("%d", a + i);
        p[a[i]] = i;
    }

    int cnt = 0;
    for (int i = 1; i <= n; i++)
    {
        if (a[i] == i)
        {
            continue;
        }

        cnt++;
        a[p[i]] = a[i];
        p[a[i]] = p[i];
    }

    if ((3 * n - cnt) % 2 == 0)
    {
        printf("Petr");
    }
    else
    {
        printf("Um_nik");
    }

    return 0;
}

Two:

// AC
//#include <iostream>
//#include <cstdio>
//
//using namespace std;
//
//int n;
//
//int main()
//{
   
// scanf("%d", &n);
//
// int cnt = 0, x;
// for (int i = 1; i <= n; i++)
// {
   
// scanf("%d", &x);
// cnt += (x == i);
// }
//
// if (cnt >= n / 1000)
// {
   
// puts("Petr");
// }
// else
// {
   
// puts("Um_nik");
// }
//
// return 0;
//}