数论废 + 脑洞不够大 = = 一上午都没搞出这道题
学会了一个新的求逆元的姿势
inv[i] = (LL)(n - n / i) * inv[n % i] % n;
强迫症不行不行的 - - 看到数学题的结论不证明出来就非常不舒服 - - 高考后遗症吗 - -
按照想到的顺序:
1. 序列开头一定是1,结尾一定是n
2.1-4可以手动构造出来
3.大于4的合数一定无解
证明:
首先由2 可知 (a1a2a3...an-1) mod n == (n - 1)! mod n
设n为大于4的合数,设n最小的质因数为k
a) 若n / k!=k 那么 n / k <n
所以 (n - 1)! mod n == (n - 1)! mod (n/k * k) == 0;
b) 若n/k == k 那么 n == k*k
因为 n > 4
所以 k > 2
所以 k * ( k - 1 ) != k
又因为 k * (k - 1) < n
所以 (n - 1)! mod ((k - 1) * k * k) == 0
所以 (n - 1)! mod n == (n - 1)! mod (k * k) = 0
综上所述 n为大于4的合数时, (a1a2a3...an-1) mod n ==0 衡成立,显然不符合题意
4.对于每个质数n 一定可以构造 prefix product sequence 为 [1,2,3,...n-1, 0]
方法: 假设已经构造好 prefix product sequence 第i-1位 为 i-1 那么要使第 i 位 为 i
只需 a[i] 满足 ((i - 1)*a[i]) mod n == i
所以 a[i] = i * inv[i-1] mod n
(因为我比较愚钝 - - 所以纠结了半天这样 a[i] 不会重复吗- -,这里证明一下)
证明(反证法):
假设 1 < i < j < n 且 ((i - 1)*a[i]) mod n == i 且 ((j - 1)*a[i]) mod n == j
则 (i - 1) * a[i] == i + k * n ( k = 0,1,2,3,....)
(j - 1) * a[i] == j + k1 * n ( k1 = 0,1,2,3,....)
两式相减
(j - i) * a[i] == j-i + (k1 - k) * n
移项
(j - i) * (a[i] - 1) == (k1 - k) * n
因为 i < j < n
所以 ( j - i ) < n
因为 n 为质数
所以 (a[i] - 1) mod n == 0
由本题题意 a[i] < n
所以 a[i] == 1
与 (i - 1) * a[i] mod n == i 矛盾
假设不成立,证明结束。
于是a[i] 不会重复,除了开头的1和结尾的n,与 2 - n-1 一定是一一对应的,也说明对于每个质数,这样一定能构造出符合题意的解
5.所以就简单了, n == 1-4 的 手动输出一下, n为大于4的合数输出No , 质数则输出 i*inv[i-1] mod n
贴代码
#pragma warning(disable:4996)
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<set>
using namespace std;
typedef long long LL;
const int maxn = 100005;
int n,inv[maxn];
int main() {
cin >> n;
if (n <= 3) {
cout << "YES" << endl;
for (int i = 1;i <= n;i++) printf("%d\n", i);
}
else if(n==4) {
printf("YES\n1\n3\n2\n4\n");
}
else {
for (int i = 2;i*i <= n;i++) if (n%i == 0) {
cout << "NO" << endl;
return 0;
}
cout << "YES" << endl;
cout << 1 << endl;
inv[1] = 1;
for (int i = 2;i < n;i++) {
inv[i] = (LL)(n - n / i) * inv[n % i] % n;
printf("%d\n", (1LL*i*inv[i - 1])%n);
}
cout << n << endl;
}
return 0;
}
滚去读初等数论了- -