소의 체조
시간 제한2초메모리 제한512 MB
N과 소수 M이 주어질 때, 모든 N!개 순열에 대해 각 순열의 위수(항등원이 될 때까지 반복한 횟수)를 곱한 값을 M으로 나눈 나머지를 구한다.
문제
Farmer John이 소들을 위해 새로운 아침 체조를 또 만들었다!
이전과 마찬가지로 Farmer John의 마리 소()가 한 줄로 서 있다. 왼쪽에서 번째 소의 이름표는 각 에 대해 이다. Farmer John은 소들이 처음과 같은 순서가 될 때까지 다음 단계를 반복하라고 시킨다.
- 길이 의 순열 가 주어졌을 때, 소들은 순서를 바꾸어, 바꾸기 전 왼쪽에서 번째였던 소가 바꾼 후 왼쪽에서 번째가 되도록 한다.
예를 들어 이면 소들은 한 단계를 수행하고 곧바로 같은 순서로 돌아온다. 이면 소들은 원래 순서로 돌아오기까지 여섯 단계를 수행한다. 각 단계 후 소들의 왼쪽에서 오른쪽 순서는 다음과 같다.
- 0단계:
- 1단계:
- 2단계:
- 3단계:
- 4단계:
- 5단계:
- 6단계:
길이 의 모든 개 순열 에 대해 필요한 단계 수의 곱을 구하라.
이 수는 매우 클 수 있으므로 답을 으로 나눈 나머지를 출력하라 (, 은 소수).
C++을 사용하는 참가자는 KACTL의 다음 코드가 도움이 될 수 있다. Barrett reduction이라 불리는 이 방법은 이 컴파일 시점에 알려지지 않은 상수일 때 를 평소보다 몇 배 빠르게 계산할 수 있게 해 준다. (아쉽게도 Java에는 이런 최적화를 알지 못한다.)
#include <bits/stdc++.h>
using namespace std;
typedef unsigned long long ull;
typedef __uint128_t L;
struct FastMod {
ull b, m;
FastMod(ull b) : b(b), m(ull((L(1) << 64) / b)) {}
ull reduce(ull a) {
ull q = (ull)((L(m) * a) >> 64);
ull r = a - q * b; // can be proven that 0 <= r < 2*b
return r >= b ? r - b : r;
}
};
FastMod F(2);
int main() {
int M = 1000000007; F = FastMod(M);
ull x = 10ULL*M+3;
cout << x << " " << F.reduce(x) << "\n"; // 10000000073 3
}
입력
첫 번째 줄에 과 이 주어진다.
출력
정수 하나를 출력한다.
힌트
각 에 대해, 다음 배열의 번째 원소는 소들이 단계를 거치게 하는 순열의 개수이다: 답은 이다.