0%

CF1030G Linear Congruential Generator

奇妙

因为每个点都是在modpi\bmod p_i意义下进行的运算,所以直觉上最多pip_i次后一定会出现循环,但是不一定是纯循环,可能会有一个留下的“柄”,如果记每一个“柄”的长度为lil_i,循环长度为cic_i,那最终答案是max(li)+lcm(ci)max(l_i)+lcm(c_i)

我们细致的考察一下这个循环关系

I. ai=0a_i=0

最简单的情况,ci=1c_i=1,当xi=bix_i=b_i时候li=0l_i=0,否则li=1l_i=1

II ai=1a_i=1

此时fi(k)=(xi+(k1)bi)modpif_i^{(k)}=(x_i+(k-1)b_i)\bmod p_ili=0l_i=0bi=0b_i=0ci=1c_i=1,否则ci=pic_i=p_i

III otherwise

ai>1a_i>1,因为pip_i是质数,所以ai1,(ai1)1a_i^{-1},(a_i-1)^{-1}均存在,那么就一定能回来,所以li=0l_i=0

既然li=0l_i=0了,那么xiaicixi+bij=0k1aijxiaicixi+bi(aici1)(ai1)1modpix_i\equiv a_i^{c_i}x_i+b_i\sum_{j=0}^{k-1}a_i^j\equiv x_i\equiv a_i^{c_i}x_i+b_i(a_i^{c_i}-1)(a_i-1)^{-1} \bmod p_i

(aici1)(xi+bi(ai1)1)0modpi(a_i^{c_i}-1)\left(x_i+b_i(a_i-1)^{-1}\right) \equiv 0 \bmod p_i

也即(aci1)0(a^{c_i}-1)\equiv0(xi+bi(ai1)1)0\left(x_i+b_i(a_i-1)^{-1}\right) \equiv 0

第二个式子与cic_i无关,所以ci=1c_i=1,考虑到cic_i需要求lcmlcm所以这样一定不优

第一个式子中的cic_i就是aia_i关于pip_i的阶,即ci=pi1c_i=p_i-1

最后

整理一下

ai=0li=1ci=1a_i=0 \Rightarrow l_i=1,c_i=1

ai=1li=0ci=pia_i=1 \Rightarrow l_i=0,c_i=p_i

ai>1li=0ci=pi1a_i>1 \Rightarrow l_i=0,c_i=p_i-1

现在我们需要决定一组aia_i,最大化max(li)+lcm(ci)max(l_i)+lcm(c_i)

直觉上因为li1l_i\le1,所以最大化lcmlcm是比较优的,所以我们可以先考虑最大化lcmlcm,然后看看能不能选11(因为每去一个因子lcmlcm至少除二,所以损失lcm换lil_i是亏的)

最大化lcmlcm是很容易的,降序考虑每个pip_i,如果当前的lcmlcm包含了pip_i,就选择ai>1a_i>1否则选择ai=1a_i=1

同时记录下lcmlcm每个质因子的最高次幂被几个cic_i达到了,检查选择的cic_i,如果有一个cic_i的全部质因子都被达到不止一次,那就选上一个11

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
#include <algorithm>
#include <cstdio>
#include <iostream>
using namespace std;
typedef long long ll;
const int N = 2000005;
const int mod = 1000000007;
ll p[N];
int max_p[N], cnt[N];
int prime[555555], pcnt;
bool pvis[N];
int n;
void getP(int n) {
for (int i = 2; i <= n; i++) {
if (!pvis[i]) {
prime[++pcnt] = i;
}
for (int j = 1; j <= pcnt; j++) {
if (prime[j] * i > n)
break;
pvis[prime[j] * i] = 1;
if (i % prime[j] == 0)
break;
}
}
}
ll qpow(int a, int k) {
ll res = 1, base = a;
while (k) {
if (k & 1) {
res = (res * base) % mod;
}
base = (base * base) % mod;
k /= 2;
}
return res;
}
inline void update(int p, int k) {
if (k > max_p[p]) {
max_p[p] = k;
cnt[p] = 1;
} else if (k == max_p[p]) {
++cnt[p];
}
}
bool check(int x) {
if (pvis[x]) {
for (int i = 1; i <= pcnt; i++) {
if (prime[i] * prime[i] > x) {
break;
}
int k = 0;
while (x % prime[i] == 0) {
x /= prime[i];
k++;
}
if (max_p[prime[i]] == k && cnt[prime[i]] == 1) {
return false;
}
}
if (x > 1 && max_p[x] == 1 && cnt[x] == 1)
return false;
return true;
} else {
if (max_p[x] == 1 && cnt[x] == 1)
return false;
return true;
}
}
ll lcm = 1;
int nya = 3;
bool vis[N];
int main() {
cin >> n;
for (int i = 1; i <= n; ++i) {
cin >> p[i];
}
sort(p + 1, p + n + 1);
getP(p[n]);
for (int i = n; i >= 1; i--) {
if (max_p[p[i]]) {
int val = p[i] - 1;
for (int j = 1; j <= pcnt; j++) {
if (prime[j] * prime[j] > val)
break;
int k = 0;
while (val % prime[j] == 0) {
val /= prime[j];
k++;
}
k != 0 ? update(prime[j], k) : (void)nya;
}
if (val > 1) {
update(val, 1);
}
vis[i] = 1;
} else {
update(p[i], 1);
}
}
for (int i = 1; i <= pcnt; i++) {
if (max_p[prime[i]]) {
lcm = 1ll * lcm * qpow(prime[i], max_p[prime[i]]) % mod;
}
}
for (int i = 1; i <= n; i++) {
if ((vis[i] && check(p[i] - 1)) || (!vis[i] && check(p[i]))) {
lcm = (lcm + 1) % mod;
break;
}
}
cout << lcm;
}

#数学
#数论