题意
有 n n 个人围成一圈进行“猴子选大王”游戏, 个 k k 个数,第 个人出圈。给定一个长度为 n n 的数组 , ai a i 表示刚开始编号为 i i 的人是出圈的第 个人,求最小的 k k 值。
思路
按 从小到大求出每个人的 CRT C R T ,再一一合并即可。这个人在圈内的位置记作此人 CRT C R T 的 A A ,表示第一次数到这个人时共数了 个人;此时圈中的人数记作此人 CRT C R T 的 P P ,表示每数 个人为一个循环。
代码
#include<iostream>
#include<cmath>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#define FOR(i,x,y) for(int i=(x);i<=(y);i++)
#define DOR(i,x,y) for(int i=(x);i>=(y);i--)
typedef long long LL;
using namespace std;
struct CRT
{
LL A,P;
LL gcd(LL a,LL b){return b?gcd(b,a%b):a;}
void exgcd(LL a,LL b,LL &x,LL &y)
{
if(!b){x=1,y=0;return;}
exgcd(b,a%b,y,x);y-=a/b*x;
return;
}
LL Exgcd(LL A,LL B,LL C)
{
LL k1,k2,g=gcd(A,B);
if(C%g)return -1;
A/=g,B/=g,C/=g;
exgcd(A,B,k1,k2);
return (k1*C%B+B)%B;
}
bool kase_insert(CRT _)
{
LL res=Exgcd(P,_.P,_.A-A);
if(!~res)return 0;
A+=res*P;
P=P/gcd(P,_.P)*_.P;
return 1;
}
LL ask(){return A?A:P;}
};
int main()
{
int T;
scanf("%d",&T);
while(T--)
{
int n,a[23],_a[23];
scanf("%d",&n);
FOR(i,1,n)scanf("%d",&a[i]);
int leave=n;bool flag=0;
CRT sum;
FOR(i,1,n)
{
FOR(j,1,leave)if(i==a[j])
{
if(i==1)sum=(CRT){j,leave};
else if(!sum.kase_insert((CRT){j,leave}))flag=1;
if(flag)break;
FOR(k,1,leave-1)_a[k]=a[(j+k-1)%leave+1];
FOR(k,1,leave-1)a[k]=_a[k];
leave--;
break;
}
if(flag)break;
}
printf(flag?"Creation August is a SB!\n":"%lld\n",sum.ask());
}
return 0;
}