user

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
114
115
116
117
118
119
不幸素数
定义数n对应的不幸素数u(n)为最小的、使得n除以p的余数(即n%p)不是7的倍数的素数p。
例如,u(14) = 3,u(147) = 2,u(1470) = 13。

记U(N)为求和\sum_{n = 1}^N u(n)。
已知U(1470) = 4293。

求U(10^{17})。

我的思路:首先对于所有奇数答案就是2,因为奇数%2=1一定不是7的倍数.
对于偶数可以考虑容斥,我给你下面暴力计算的数据:

2 3
4 3
6 5
8 3
10 3
12 5
14 3
16 3
18 5
20 3
22 3
24 5
26 3
28 3
30 7
32 3
34 3
36 5
38 3
40 3
42 5
44 3
46 3
48 5
50 3
52 3
54 5
56 3
58 3
60 7
62 3
64 3
66 5
68 3
70 3
72 5
74 3
76 3
78 5
80 3
82 3
84 5
86 3
88 3
90 7
92 3
94 3
96 5
98 3
100 3
102 5
104 3
106 3
108 5
110 3
112 3
114 5
116 3
118 3
120 7
122 3
124 3
126 5
128 3
130 3
132 5
134 3
136 3
138 5
140 3
142 3
144 5
146 3
148 3
150 7
152 3
154 3
156 5
158 3
160 3
162 5
164 3
166 3
168 5
170 3
172 3
174 5
176 3
178 3
180 7
182 3
184 3
186 5
188 3
190 3
192 5
194 3
196 3
198 5
200 3
202 3
204 5
206 3
208 3
210 11

给出cpp代码.

deepseek v4 思考

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
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
我们被问到:“不幸素数”的定义,以及求U(N) = sum_{n=1}^N u(n),其中u(n)是最小的素数p使得n % p不是7的倍数。已知U(1470)=4293,验证数据给出了一些值,最后要求U(10^17)。需要给出C++代码。

首先,理解问题:
对于n,u(n)是最小的素数p,使得n mod p ≠ 0 mod 7的倍数?原文:“使得n除以p的余数(即n%p)不是7的倍数的素数p”。7的倍数包括0, 7, 14, ... 但余数是介于0到p-1之间的,所以对于小于7的素数,余数不可能是7的倍数(除非余数为0,0是7的倍数)。所以:
对于p=2:余数只能是0或1。0是7的倍数,1不是。所以当n为奇数时,n%2=1,不是7的倍数,所以u(n)=2。
对于p=3:余数0,1,2。0是7的倍数,1和2不是。所以n%3 != 0时u(n)≤3。但如果n%2=0(偶数),则p=2时余数为0,是7的倍数,所以p=2不行,需要看p=3。如果n%3≠0,则u(n)=3。如果n%3=0呢?就要看p=5,余数可能是0..4,0是7的倍数,其他不是。所以当n同时是2和3的倍数时,n%5如果非0则u(n)=5,如果n%5=0则继续p=7。对于p=7:余数0..6,其中0是7的倍数,其余1..6不是7的倍数。所以如果n%7≠0,则u(n)=7。如果是7的倍数,那么余数为0是7的倍数,所以p=7不行,继续p=11。对于p=11,余数0..10,其中0和7是7的倍数,其余不是。所以只要余数不是0或7,u(n)就可以是11,否则继续。

因此,u(n)实际上是找到最小的素数p,使得n mod p ≠ 0 且 n mod p ≠ 7? 注意:只有当p>7时,余数才可能出现7。对于p=7,余数最大为6,所以只需检查0。对于p<7,只需检查0。

总结:
u(n) = 最小的素数p,满足:

若p=2: n%2 ≠ 0

若p=3: n%3 ≠ 0

若p=5: n%5 ≠ 0

若p=7: n%7 ≠ 0

若p=11: n%11 ≠ 0 且 n%11 ≠ 7

若p=13: n%13 ≠ 0 且 n%13 ≠ 7

若p=17: n%17 ≠ 0 且 n%17 ≠ 7, 等等。

但注意:当p>7时,还需要考虑余数为7是7的倍数。所以条件是:n % p ≠ 0,并且如果p > 7,还要 n % p ≠ 7。对于p ≤ 7,余数7不可能出现。

为了验证,看例子:u(14) = 3。14%2=0(是7的倍数),14%3=2(不是7的倍数),所以为3。正确。
u(147)=2。147奇数,%2=1,所以为2。正确。
u(1470)=13。1470是偶数,%2=0;1470%3=0(1+4+7+0=12);%5=0;%7=0(1470/7=210);%11:1470 = 11*133 + 7,余数为7,是7的倍数,不行;%13:1470 = 13*113 + 1,余数为1,不是7的倍数,所以是13。正确。

现在我们要求U(N) = sum_{n=1}^N u(n) 对于 N = 10^17。这显然是一个大数,需要数学分析,不能暴力。需要写C++代码来计算它。这应该是一个算法题,要求输出代码和结果?提示说“给出cpp代码”,也就是写一个程序来计算U(10^17)。可能是Project Euler类型的问题。我们需要设计一个高效算法。

分析U(N):
N=10^17很大。我们需要基于容斥原理或中国剩余定理来计算满足不同u(n)值的n的数量。

首先,对于奇数 n,u(n)=2。所以贡献:所有奇数个数 = ceil(N/2) 或 floor((N+1)/2),每个贡献2。

对于偶数 n,u(n) >= 3。我们需要找出最小的素数p满足条件。这类似筛法。我们可以通过逐步筛选:对于偶数n,我们在素数序列中检查,跳过那些使n%p为0或(当p>=11时)7的素数。实际上,这等价于寻找最小的p,使得n不属于某些同余类。

我们可以分类统计:
令 S 为所有偶数 n ≤ N。
其中 u(n)=3 的 n 满足:n%2=0,n%3≠0。
u(n)=5 的 n:n%2=0,n%3=0,n%5≠0。
u(n)=7 的 n:n%2=0,n%3=0,n%5=0,n%7≠0。
u(n)=11 的 n:n%2=0,3,5,7均为0,且 n%11≠0 且 n%11≠7。
u(n)=13 的 n:n%2..11满足相应条件(即对于2,3,5,7余0;对于11余0或7),且 n%13≠0且≠7。
等等。

这可以形式化为:对于每个素数p,我们求满足“对于所有 q < p 的素数,条件失败;对于 p,条件成功”的 n 的数量。

定义条件 C(q) 对于 n:
若 q=2: n%2 ≠ 0? 不,失败条件是 n%2==0。
我们定义“失败”为:当检查q时,n不满足条件。所以u(n)=p意味着:对于所有素数 q < p,n%q 是7的倍数(当q>7时,可能是0或7;当q<=7时,只能是0),而 n%p 不是7的倍数(即不是0且当p>7时也不是7)。

换句话说,n 必须属于模每个素数的特定剩余类:
对于 q=2: n ≡ 0 (mod 2) (失败)
对于 q=3: n ≡ 0 (mod 3) (失败)
对于 q=5: n ≡ 0 (mod 5) (失败)
对于 q=7: n ≡ 0 (mod 7) (失败)
对于 q=11: n ≡ 0 或 7 (mod 11) (失败)
对于 q=13: n ≡ 0 或 7 (mod 13) (失败)
...
对于 q=p: n 不能 ≡ 0 或 7 (mod p) (如果 p>7),或对于 p<=7不能 ≡ 0。

所以,对于 u(n)=p 的 n,n 必须满足:
对于所有素数 q < p,n 在同余类集合 Fail_q 中,其中:

若 q=2,3,5,7: Fail_q = {0}

若 q>=11: Fail_q = {0, 7}
且 n 不在 Fail_p 中。

因此,我们可以用包含-排除原理(或中国剩余定理+计数)来计算满足这些同余条件的 n 的数量,因为不同的素数的条件可以通过中国剩余定理合并,模数为这些素数的乘积。

但是随着p增大,素数乘积增长非常快。我们需要计算直到多大的p?对于N=10^17,最大的u(n)会有多大?我们需要找出最大的p,使得存在某个 n ≤ N,其u(n)=p。由于p是满足条件的最小素数,这意味着n必须同时满足所有小于p的失败条件。因此n是所有小于p的素数乘积的倍数,或者满足一些同余条件。

我们先估计最大可能的p。对于每个n,u(n)至少为2。对于偶数n,u(n)可能较大。最坏情况是 n 是很多小素数的倍数,且对于p>=11还满足余数为0或7。由于N=10^17,最大可能的p是多少?一个偶数n最多能是多少个素数的公倍数?考虑 n 满足对于所有素数 q < p 都属于 Fail_q 的最小正数。这类似于寻找最小的n使得对于所有q<p满足条件,然后看它是否≤N。这样的最小n可能很大。

首先,Fail集合的模条件:
对于q=2,3,5,7,必须余0 => n是2*3*5*7=210的倍数。
对于q=11,余0或7 => 模11的两个类。
对于q=13,余0或7 => 模13的两个类。
等等。因此满足所有q<p失败条件的n的存在区间大小是模 M = 210 * 11 * 13 * ... 的剩余类个数 = 1*1*1*1*2*2*... = 2^{k},其中k是q在11到p之间的素数个数。

最小的正n可能有多大?大约平均分布在模M的各个剩余类中。M增长极快。N=10^17,所以p不会太大。实际上,前几个素数乘积:2*3*5*7=210;*11=2310;*13=30030;*17=510510;*19=9699690;*23=223092870;*29=6469693230;*31=200560490130;*37=7420738134810;*41=304250263527210;*43=13082761331670030;*47=614889782588491410 > 6e17。这只是全为0的情况(对于11以上选0类)。如果选择7类,可能某些剩余类更小。但最大p可能大概在47左右,因为全0类乘积在47时已经超过10^17了。但还有选7的类,可能使得满足条件的最小n更小?不一定,我们只需存在某个n≤N满足所有失败条件,且对于p失败条件不满足。最大u(n)可能达到p=53或更大?我们需要仔细估计。

不过,我们不需要动态计算到未知的p。我们可以从p=2开始,逐个排除n,直到所有n都被分配了u(n)。即我们可以用筛法思想:开始所有n的u(n)未定。从最小素数开始,检查n是否满足条件。这种筛法类似于埃拉托色尼筛法。对于N=10^17,我们无法直接筛,但我们可以用数学公式计算满足条件的数量,并用容斥或直接计算每个p贡献。

由于N巨大,但涉及的素数个数很少(大概最多到50左右的素数),我们可以通过遍历所有可能的同余类组合来计算。但我们不能遍历整个N,需要公式计算在1..N中满足特定同余条件的数的个数。

我们可以定义:
设P是素数集合。对于每个p,我们需要计算满足“对所有 q < p,n ≡ a_q (mod q),且 n ≡ b (mod p) 不在Fail_p中”的n的个数,其中a_q ∈ Fail_q。但实际上,u(n)=p的n的集合是:
所有满足对于q<p,n mod q ∈ Fail_q,但 n mod p ∉ Fail_p 的 n 的并集,即所有可能的a_q组合。我们可以用中国剩余定理计算每种组合下的n个数,然后加起来。

注意:对于q<p,失败条件要求n mod q ∈ Fail_q。这个条件可以合并为 n ≡ r (mod M),其中 M = ∏_{q<p} q,r是某些剩余类。实际上,M是这些模数的乘积。因为2,3,5,7,11,...都是素数,两两互素,所以模M的剩余类个数为 Π |Fail_q|。对于q≤7,|Fail|=1;对于q≥11,|Fail|=2。所以总组合数为 2^{π(p-1)-4},其中π(p-1)是小于p的素数个数。

由于p最多约50,π(50)=15。2^{11}=2048。组合数量不大。对于每个给定的p,我们可以遍历所有可能的剩余类组合(即指定每个q<p的a_q),用CRT计算该组合下n的模M的值r。然后对于每个r,我们需要计算满足 n ≡ r (mod M) 且 n mod p ∉ Fail_p,且 n ≤ N 的n的个数。由于p可能不整除M,我们可以将条件合并为模Mp,或者直接计算:对于每个r,n = kM + r ≤ N。k的范围是 0 ≤ k ≤ floor((N-r)/M)(如果r>0且r≤N)。然后我们需要统计这些k中有多少满足 (kM + r) mod p ∉ Fail_p。这等价于 kM ≡ (x - r) mod p 对于哪些x不在Fail_p中。因为p是素数且M与p互素(M是小于p的素数乘积,p不在其中),所以k mod p 有一个双射。所以满足条件的k的比例是 (p - |Fail_p|)/p。所以准确个数可以用除法计算。

但是注意:我们需要排除已经被更小素数筛掉的n吗?这就是定义,我们直接计算满足“对于所有q<p失败,且对于p成功”的n,这些n自然没有被任何更小的素数作为u(n),因为更小的素数需要失败条件,而p是第一个成功的。所以可以直接计数,无需额外排除。所以sum_{p} count(p) = N(所有n被分配)。但需要注意 p=2 时,条件“q<2”没有素数,所以直接就是所有n满足 n mod 2 ∉ Fail_2 = {0},即奇数。这没错。对于p=3,条件是n mod 2 = 0(失败),且 n mod 3 ≠ 0。这正好是偶数中非3倍数的数。这与我们的筛法一致。

因此,我们可以对所有可能的素数p,计算u(n)=p的n的数量,乘以p,求和,加上边界情况。

算法步骤:

生成素数列表,直到某个上界。上界如何确定?我们需要一直计算直到所有n都分配完毕。即总和达到N。因为对于任何n,只要存在一个素数p满足n mod p ∉ Fail_p,它就有最小的p。如果n满足所有素数的失败条件,那么它就没有u(n)?这可能吗?对于有限N,只要p足够大,可能有些n没有u(n)?但根据问题定义,u(n)是最小的素数使得... 一定有解吗?如果n使得对所有素数p都满足n mod p ∈ Fail_p,那么u(n)不存在。是否存在这样的n?对于每个p,Fail_p要么是{0},要么是{0,7}。如果n=0,所有素数余数0。但n从1开始。如果n=7?7 mod 2=1 (不在Fail_2),所以u(7)=2。对于n同时满足2,3,5,7的失败条件(即n是210的倍数),并且对于11满足0或7,13满足0或7等。由于我们只考虑有限N,只要p足够大,M将超过N,从而满足所有小于p的失败条件的n可能只有0,或者可能只有某些特定的数。我们需要找到足够大的p,使得满足“所有素数q < p”失败条件的n要么为0,要么>N。实际上,我们需要找到所有n ≤ N的u(n)。我们可以持续增加p,直到某一步找不到任何n ≤ N满足条件为止。也就是当M > N 且 对于所有r满足条件的n都>N时,即可停止。

我们如何判断结束?我们可以累加每个p贡献的count,直到总count = N。由于p的贡献是正的,当累计达到N时,后面的p贡献为0。我们可以设定一个足够大的素数上限,比如100,或动态检测:当对于当前p,所有可能的组合的最小正数r_min > N时,即可停止。

现在我们需要实现CRT计算。我们有多个模数,由于是素数且两两互素,模M = product。M可能非常大(超过2^64?因为47个素数乘积~6e17,再乘以后面的素数可能超过64位)。我们需要使用128位整数或大整数。N=10^17 < 2^63-1 (9.22e18),所以用64位无符号整数可以存N,但M可能超过64位。例如所有素数到53的乘积:2*3*5*7*11*13*17*19*23*29*31*37*41*43*47*53。计算一下大概多大。2*3*5*7=210,11*13=143,17*19=323,23*29=667,31*37=1147,41*43=1763,47*53=2491。乘积约为 210*143*323*667*1147*1763*2491 ≈ ? 我们可以估计:之前47乘积6.14e17,乘以53=3.25e19,这已经超过64位(2^64≈1.84e19)。但N=10^17,我们是否需要处理p=53?可能有些n的失败组合使用7的类,使得r更小,所以M可能不需要全乘积,但计算CRT过程中需要使用模乘积,可能会超出64位。可以用__int128(GCC/Clang支持)或自己写简单大整数(只需要乘法和比较)。由于只有十几个素数,我们可以用Python或C++的boost多精度?要求给C++代码,我们可以使用__int128,它在大多数竞赛环境中可用(如GCC, Clang)。如果不允许,我们可以手动实现128位或只用64位并用数学方法避免溢出?我们可以使用模算术计算r,但需要知道确切的r值以判断r ≤ N。我们可以用中国剩余定理合并时保持模乘积M,当M超过N很多时,我们不需要精确的r,只需知道最小的正r是否>N?因为只要最小r>N,则该类没有n≤N。我们可以动态检查:在CRT合并过程中,我们可以计算当前部分模数下的最小非负解的范围?或者我们可以使用深度优先搜索,在搜索树中剪枝:对于给定的素数序列,我们选择每个q的余数(0或7),我们可以计算当前的部分模数M_part和当前的部分余数r_part。然后对于下一个素数q,我们有两个可能的余数a。我们可以用扩展欧几里得或逐步合并。如果在某一步,当前的部分M_part已经 > N,但我们需要检查是否存在解 ≤ N。我们可以用中国剩余定理逐步构建解。

另一个方法:由于我们需要对所有n≤N求和u(n),我们可以用类似“筛法”公式直接计算总和,而不显式地计算每个组合的最小r。我们可以利用数学公式:
对于每个p,计数C(p) = #{ n ≤ N : n mod q ∈ Fail_q ∀ q < p, and n mod p ∉ Fail_p }。
这个计数可以用容斥原理直接写为:
C(p) = 满足 n mod q ∈ Fail_q (∀ q < p) 的数量 - 满足 n mod q ∈ Fail_q (∀ q < p) 且 n mod p ∈ Fail_p 的数量。
即 C(p) = A(<p) - A(<p ∪ {p}),其中A(S) = #{ n ≤ N : n mod q ∈ Fail_q ∀ q ∈ S }。

那么A(S) 就是对于给定的素数集合S,每个有Fail集合,求满足同余条件的n的数量。这可以写为:
A(S) = Σ_{r ∈ R} floor((N - r)/M) + 1(如果r≤N),其中R是模M的所有满足条件的剩余类,M = ∏_{q∈S} q。
因为所有q互素,R的大小 = ∏ |Fail_q|。

对于每个S,我们不需要显式枚举所有组合吗?我们需要对每个p计算C(p)。我们有:
U(N) = Σ_{p} p * C(p) = Σ_{p} p * (A(<p) - A(≤p)),其中≤p表示包含p。
这可以重排为 Σ_{p} A(<p) ?不对,因为这是望远镜求和:
设 A_0 = N(没有条件,所有n)。
对于递增的素数 p_1=2, p_2=3, p_3=5, ... 设 A_k = A({p_1, ..., p_k})。
那么 C(p_k) = A_{k-1} - A_k。
U(N) = Σ_k p_k * (A_{k-1} - A_k) = p_1 A_0 + Σ_{k≥2} (p_k - p_{k-1}) A_{k-1} - ... 需要仔细:
p_1 A_0 - p_1 A_1 + p_2 A_1 - p_2 A_2 + ... = p_1 A_0 + Σ_{k≥2} (p_k - p_{k-1}) A_{k-1} - lim (p_k A_k)。
当k足够大时,A_k = 0,所以极限为0。
所以 U(N) = 2 * N + Σ_{k=2}^{∞} (p_k - p_{k-1}) * A_{k-1}.
其中 p_1=2, p_2=3, p_3=5, p_4=7, p_5=11, p_6=13, ...
A_{k} = 满足对于所有 i ≤ k,n mod p_i ∈ Fail_{p_i} 的 n 的数量。

这非常漂亮!我们只需要计算 A_k 对于 k = 0,1,2,... 直到 A_k = 0。
A_0 = N.
A_1 = #{ n ≤ N : n ≡ 0 mod 2 } = floor(N/2).
A_2 = #{ n ≤ N : n ≡ 0 mod 2, n ≡ 0 mod 3 } = floor(N/6).
A_3 = #{ n ≤ N : n ≡ 0 mod 2,3,5 } = floor(N/30).
A_4 = #{ n ≤ N : n ≡ 0 mod 2,3,5,7 } = floor(N/210).
A_5 = #{ n ≤ N : n ≡ 0 mod 2,3,5,7, n ≡ 0 or 7 mod 11 }.
这里开始,Fail集包含两个余数。A_5需要计算满足 n ≡ 0 mod 210, 且 n mod 11 ∈ {0,7} 的n个数。
一般地,A_k = Σ_{r ∈ R_k} floor((N - r)/M_k) + [r ≤ N],其中 M_k = ∏_{i=1}^k p_i,R_k 是模 M_k 满足条件的剩余类集合。

现在,M_k 增长非常快。10^17以内,k最多到多少?M_4 = 210。M_5 = 2310。M_6 = 30030。M_7 = 510510。M_8 = 9699690。M_9 = 223092870。M_10 = 6469693230。M_11 = 200560490130。M_12 = 7420738134810。M_13 = 304250263527210。M_14 = 13082761331670030。M_15 ≈ 6.15e17。M_16 ≈ 3.25e19 > 10^17。所以 k 最大大概在15或16。也就是我们只需要处理到大概 p=47 或 53。所以素数个数非常少(大约15个)。我们可以显式枚举所有剩余类组合来计算A_k。每个A_k的组合数为 1*1*1*1*2^{k-4} = 2^{k-4}。对于k=15,2^{11}=2048,完全可以接受!所以我们可以直接对于每个k,枚举所有符合条件的剩余类,计算满足条件的n的数量,求和得到A_k。然后利用公式求和 U(N) = 2*N + Σ_{k=2}^{K} (p_k - p_{k-1}) * A_{k-1},直到A_{K}=0停止。其中K是使得A_K=0的最小索引(因为M_K > N且没有满足条件的r≤N)。

我们来验证这个公式:
U(N) = Σ p_k * C(p_k)
其中 C(p_k) = A_{k-1} - A_k
U(N) = 2(N - A_1) + 3(A_1 - A_2) + 5(A_2 - A_3) + 7(A_3 - A_4) + 11*(A_4 - A_5) + ...
= 2N - 2A_1 + 3A_1 - 3A_2 + 5A_2 - 5A_3 + 7A_3 - 7A_4 + 11A_4 - 11A_5 + ...
= 2N + A_1 + 2A_2 + 2A_3 + 4A_4 + ...? 让我们计算系数:
A_0: 2
A_1: -2+3 = 1
A_2: -3+5 = 2
A_3: -5+7 = 2
A_4: -7+11 = 4
A_5: -11+13 = 2
A_6: -13+17 = 4
A_7: -17+19 = 2
A_8: -19+23 = 4
A_9: -23+29 = 6
A_10: -29+31 = 2
A_11: -31+37 = 6
A_12: -37+41 = 4
A_13: -41+43 = 2
A_14: -43+47 = 4
A_15: -47+53 = 6 (如果还有)

一般地,U(N) = 2N + Σ_{k=1} (p_{k+1} - p_k) * A_k? 让我们重新推导:
U(N) = Σ_{k=1} p_k (A_{k-1} - A_k) (设A_0 = N)
= p_1 A_0 + Σ_{k=1} (p_{k+1} - p_k) A_k - lim_{K->∞} p_K A_K.
因为A_K最终为0,所以 U(N) = 2N + Σ_{k=1} (p_{k+1} - p_k) A_k.
检查:k=1: p_2-p_1 = 3-2=1; A_1 = #{偶数} = N/2。如果N=1: A_1=0。如果N=2: A_1=1。U(2): u(1)=2, u(2)=3? 根据定义:u(1)=2 (奇数), u(2): 2%2=0 (7倍数), 2%3=2 (不是7倍数) -> u(2)=3。U(2)=5。公式:2*2 + 1*A_1 = 4 + 1*1 = 5。对!
k=2: p_3-p_2 = 5-3=2; A_2 = #{6的倍数}。k=3: p_4-p_3 = 7-5=2; A_3 = #{30倍数}。k=4: p_5-p_4 = 11-7=4; A_4 = #{210倍数}。k=5: p_6-p_5=13-11=2; A_5 = #{满足...}。等等。
所以公式 U(N) = 2N + Σ_{k=1}^{K} (p_{k+1} - p_k) * A_k,其中A_K是最后一个非零项,我们可以一直计算直到A_k = 0。

现在我们只需计算 A_k 对于 k=1,2,... 直到0。A_k 是满足对于 i=1..k, n ≡ Fail_{p_i} 的 n 的数量。这里 Fail_2={0}, Fail_3={0}, Fail_5={0}, Fail_7={0}, Fail_11={0,7}, Fail_13={0,7}, Fail_17={0,7}, 等等。

如何高效计算 A_k?由于k最多15,我们可以用递归/回溯遍历所有可能的余数组合。对于给定的k,素数列表 P = [2,3,5,7,11,13,17,...]。我们要找满足 n ≡ r_i (mod p_i) 的 n 的数量,其中 r_i ∈ Fail_{p_i}。总组合数为 2^{max(0, k-4)}。对于每个组合,我们可以用中国剩余定理计算模 M = ∏_{i=1}^k p_i 的剩余类 r。然后该类的数量为 floor((N - r)/M) + 1(若 r ≤ N 且 r > 0? 如果 r=0,则 n=0 可能?n从1开始,所以我们需要小心n=0的情况。0是否被计入?题目求和从n=1到N。如果r=0,则n=0满足条件,但n=0不在[1,N]范围内。所以对于r=0,最小正n是M。数量为 floor((N)/M) - 1 + 1? 其实 floor((N - r)/M) 对于r=0: floor(N/M),其中包括n=M, 2M,... 不包括0。所以公式 floor((N - r)/M) + (r != 0 ? 1 : 0) 对于 r ≤ N? 等等。标准计数:在[1, N]中满足 n ≡ r (mod M) 的数量,其中0 ≤ r < M。
如果 r == 0:n = M, 2M, ... ≤ N,个数 = floor(N/M)。
如果 r > 0:n = r, r+M, ... ≤ N,个数 = floor((N-r)/M) + 1 (如果 r ≤ N) 否则 0。
可以统一写成:个数 = (r == 0) ? (N / M) : ((N >= r) ? (N - r) / M + 1 : 0)。需要注意整数除法。

由于组合数最多2048,每个组合进行CRT得到r和M。M是乘积,可能超过64位整数。在CRT过程中,我们需要合并同余方程。我们可以逐步合并:
r = 0, M = 1
for each prime p_i:
M = M * p_i
我们需要找到新的 r 满足 r ≡ r_old (mod M_old) 且 r ≡ a_i (mod p_i),其中a_i ∈ {0} 或 {0,7}。
由于我们枚举所有组合,我们可以用深度优先搜索(DFS),在每一步扩展两个分支(对于p_i ≤7只有1个分支),并更新当前的模数和余数。在DFS过程中,我们可以剪枝:如果当前的部分M已经 > N 并且当前的最小正解 > N,则可以直接计数0并剪枝。但为了简单,我们完全枚举组合也是可以的,因为总数小。

但要注意:M可能变得非常大,比如当k=15时 M ~ 6e17,还能存储在64位内(最大约9e18)。k=16时 M ~ 3e19 > 2^64,超出了64位无符号整数(最大1.84e19)。但也超出了?2^64 = 1.8446744e19,3.25e19 > 2^64。所以我们需要使用 __int128 或者处理比64位更大的整数。在C++中,可以使用 __int128(GCC/Clang)或 boost::multiprecision::cpp_int。考虑到题目要求给出cpp代码,很可能是在有__int128的评测环境下,或者我们可以用Python?题目要求cpp代码。我们可以使用 __int128。或者我们可以用多个64位字长,但为了简洁,使用 __int128 是一个好选择。

或者我们可以不直接计算M,而使用浮点数?不行,需要精确。我们可以把DFS设计为:当M > N时,我们不需要具体的r,而只需知道是否可能存在 ≤ N 的解。实际上,对于给定的部分M_part和r_part,以及后续还需要满足的条件,我们可以判断最小正解是否可能 ≤ N。我们可以采用递归计算数量,而不是先求最终M和r。比如我们有模数序列和余数选项,我们想统计满足条件且 ≤ N 的n的个数。这可以用“数位DP”或“容斥”结合中国剩余定理。直接CRT合并是最简单的。

关于 __int128 的使用:在C++中,我们可以写 using int128_t = __int128; 并自己实现打印输出,或者如果只需要计算最终结果,结果U(10^17)可能是一个很大的数,但肯定小于 10^17 * 最大素数 (~50) = 5e18 < 2^63-1? 2^63-1 ≈ 9.22e18,所以U(10^17)可以用64位有符号或无符号存储。但中间计算M可能超过64位。我们可以用 __int128 存储M和r。

实现步骤:

生成素数列表。我们需要多少个素数?直到A_k变为0。A_k = 满足条件的n的数量。当 M_k 的最小可能正剩余 > N 时,A_k = 0。我们可以预生成素数直到足够大,比如100以内的素数,共25个。我们按需取用。
素数列表:2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53,59,61,67,71,73,79,83,89,97。

对于每个k从1开始,计算 A_k = count(条件对于前k个素数)。直到A_k == 0。

根据公式 U = 2*N + Σ_{k=1}^{K-1} (p_{k+1} - p_k) * A_k。 (注意K是第一个A_K=0的索引,那么我们求和到A_{K-1}为止,因为A_K=0对求和无贡献,p_{K+1}不存在?公式是到无穷,但A_K=0后面都是0。所以我们需要p_{k+1}对于k=K-1。所以素数列表需要至少到第K个。当A_K=0时,k最大为K-1,需要p_{K}和p_{K+1}?实际上,U(N) = 2N + Σ_{k=1}^{∞} (p_{k+1}-p_k)A_k。由于A_K=0,k≥K的项都是0。所以我们只需要k=1到K-1的和,这需要p_2..p_K。因此素数列表只需生成到p_K(即第K个素数)。p_K是使得A_K=0的第一个索引。那么我们需要确定K。

但实际上,A_k 定义是前k个素数条件都失败。那么当k增加,A_k递减。当M_k的最小正解 > N时,A_k=0。所以我们可以循环k=1,2,...,计算A_k,如果A_k == 0,则停止,此时k_max = k-1。我们需要素数到p_{k_max+1}?注意求和中项 (p_{k+1}-p_k)*A_k,所以当A_{k_max}是最后一个非零时,我们需要p_{k_max+1}。如果A_{k_max+1}=0,我们不需要加它的项(因为A=0)。所以我们需要素数直到 p_{k_max+1}。我们可以在生成素数时多生成几个。

计算 A_k 的方法:
我们需要计算满足以下条件的 n ∈ [1, N] 的数量:
n ≡ 0 (mod 2)
n ≡ 0 (mod 3)
n ≡ 0 (mod 5)
n ≡ 0 (mod 7)
对于 i=5..k (11,13,...): n mod p_i ∈ {0, 7}。

我们可以写一个递归函数:
count(prime_index, current_mod, current_remainder, N)
但这样每一步合并模数可能会产生很大的模数。

更好的方法:因为总组合数只有2048(对于k=15),我们可以预先计算最终的M和所有可能的r。
但合并时我们需要计算 r (mod M)。我们可以写一个通用的CRT函数,接受模数和余数的向量,返回 (r, M)。但是向量长度可变,且M可能超过64位。

我们也可以用另一种方法:直接利用 floor 函数的性质。由于M = ∏ p_i,剩余类数量为 2^{k-4}。对于每个组合,r 可以通过以下方式计算:
我们枚举所有可能的余数序列 a_1..a_k,其中 a_i ∈ {0} 或 {0,7}。
我们可以用搜索,在每一步维护 (r, M)。当M > N时,我们可以不继续乘吗?不,我们需要最终的r,因为即使M很大,也可能存在某个满足条件的n ≤ N。例如 M=1000, r=5,N=10,那么没有n≤10。如果r=3, N=10,有n=3。所以即使M > N,只要r ≤ N,仍有1个解(即n=r)。如果r > N,则0解。而如果M > N且r=0,则最小正解是M > N,所以0解。因此,只要M > N,最多只有一个解(当0 < r ≤ N时有一个解)。所以我们可以在搜索过程中,一旦当前M > N,我们不需要继续合并后续素数?不对!如果当前M > N,但仍有后续素数条件需要满足。如果当前我们已经有一个部分余数r_part满足所有已处理条件,且部分模数M_part > N。现在我们要加上新条件:n ≡ a (mod p)。由于M_part > N,满足前序条件的n至多只有一个,即r_part(如果r_part ≤ N)。那么我们只需检查r_part是否满足新条件即可!如果满足,那么它仍是解,否则变为0解。这样可以大大简化计算。这类似于:当我们合并同余方程时,解是模 M = lcm(M_part, p) 的某个剩余类。由于 M_part > N,且 M_part 与 p 互素,新的 M = M_part * p > N。在这个大模数下,解在[1,N]范围内最多只有一个(即如果 r_new ≤ N,则有一个,否则0)。而r_new 就是满足 r_new ≡ r_part (mod M_part) 且 r_new ≡ a (mod p) 的数。因为M_part > N,所以r_part就是那个唯一可能的候选(如果r_part ≤ N),我们只需检查 r_part ≡ a (mod p) 是否成立。如果成立,r_new = r_part;否则无解。因此我们可以用DFS在搜索过程中动态判断,避免大数乘法超出范围,也避免不必要的计算。

太好了!我们可以写一个递归函数:
// primes: 全局素数数组,索引从0开始
// k: 目标处理的素数个数
long long dfs(int idx, int k, long long M, long long r, long long N)
其中 idx 是当前正在处理的素数索引(从0开始)。我们要处理前k个素数。我们正在选择第 idx 个素数的余数。
如果 idx == k: 返回计数 count_n(r, M, N)。
在递归中:
如果 M > N:
如果 r == 0: 0个(因为最小正解是M > N)
如果 r > N: 0个
否则 (0 < r <= N): 我们有一个候选 r。我们需要继续满足后面所有素数的条件。我们可以直接检查 r 对于 idx 到 k-1 的每个素数 p,是否满足 r % p ∈ Fail_p。如果全部满足,则返回1;否则返回0。这样就不用继续递归了。

这非常高效!M很快就会超过10^17(实际上前几个素数乘积 M_4=210, M_5=2310,M_6=30030,M_7=510510,M_8=9699690,M_9=223092870,M_10=6469693230,M_11=200560490130 > 2e11, M_12=7.4e12,M_13=3e14,M_14=1.3e16,M_15=6e17, M_16=3.25e19>1e17。所以对于k<=14,M <= 1.3e16 < 10^17。k=15时,M约6e17 < 10^17? 6e17 > 10^17! 6.14e17 > 1e17。所以M_15 > N。所以我们很快会遇到 M > N。实际上,我们只需要使用64位整数即可,因为 M 最大可能为 6e17 或更大(如果k更大,递归会提前剪枝,不会再计算更大的M)。我们仍需要计算当M <= N时的计数,M最大约1.3e16(对于k=14)或6e17(对于k=15但可能在M超过N时被剪枝)。实际上,对于k=15,M_product = M_15 ≈ 6.14e17 > 10^17,但我们在处理前15个素数时,当合并到第15个素数时,M会变成 > N。我们可以用64位无符号整数存储M和r,只要保证在乘法时不会溢出?6e17 * 下一个素数 (53) 会溢出,但我们在乘法之前会判断 M > N 从而不会执行乘法。所以我们只需要确保在 M <= N 时才做乘法 M * p。M <= N = 10^17,p <= 53,乘积 <= 5.3e18 < 2^63-1 (9.22e18)。所以 64位有符号或无符号足够。为了安全,使用 unsigned long long 或 __int128。但 unsigned long long 最大 1.84e19,所以 5.3e18 安全。我们甚至可以使用 unsigned long long 来存储 M 和 r。但注意:在计算 floor((N - r)/M) 等可能涉及有符号/无符号。我们可以直接用 signed long long,最大 9.22e18 > 5.3e18,所以也安全。在C++中,long long 最大 9.22e18。我们将使用 long long 和 unsigned long long 按需选用。N=10^17 在 long long 范围内。

让我们细化算法:

输入:N = 10^17。
素数列表:我们用筛法生成素数到100左右足够。 primes = {2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97};

我们需要计算 A[0] = N。但公式中是 U = 2*N + sum_{k=1} (p_{k+1} - p_k) * A_k。
我们需要 A_k 对于 k = 1, 2, ... 直到0。
设 p_index 从1开始(对应第一个素数2)。但我们用0-indexed。令 primes[0]=2, primes[1]=3, etc.

A_k 对应于前 k 个素数条件都失败的数量。k 从 1 开始。A_1 = 满足 primes[0] 失败的数量。
我们要计算 A[1], A[2], ... 直到 A[K] == 0。

对于每个 k:
A_k = 0
我们使用递归函数计算满足前 k 个素数条件的 n 的数量。
递归函数原型:
void dfs(int idx, int k, long long M, long long r, long long N, long long &count)
idx: 当前要决定的素数索引 (0 到 k-1)
如果 idx == k: count += count_n(r, M, N); return;
如果 M > N:
如果 r == 0 或 r > N: return; (无贡献)
否则: 检查 r 是否满足 primes[idx] 到 primes[k-1] 的条件。若全部满足,count += 1; return;
否则 (M <= N):
对于 primes[idx] 的所有可能余数 a in Fail_set(primes[idx]):
计算新的 (r_new, M_new)。
如何合并?
我们有当前同余方程 x ≡ r (mod M),新方程 x ≡ a (mod p)。
我们需要求解 x ≡ r (mod M) 且 x ≡ a (mod p)。由于 M 和 p 互质(M 是之前不同素数的乘积,p 是新的素数),可以用中国剩余定理:找到 t 满足 t*M ≡ 1 (mod p),则 r_new = r + M * ((a - r) * t mod p)。然后 M_new = M * p。
注意:r_new 需要规范化为 [0, M_new) 中的值。
然后递归调用 dfs(idx+1, k, M_new, r_new, N, count)。

Fail_set:
对于 p = 2,3,5,7: {0}
对于 p >= 11: {0, 7}
注意:对于 p=7,Fail_set 是 {0},而不是 {0,7},因为 7 mod 7 = 0,余数7等价于0。所以不需要单独列出7。对于 p=11,Fail_set 包括0和7(7 < 11,所以是7)。对于更大的素数,也是0和7(只要7 < p)。当p=2,3,5时,7不小于p吗?对于p=7,7 mod 7=0,所以只需0。对于p=5,7 mod 5=2,2不是0也不是7?等等,问题定义:n除以p的余数不是7的倍数。7的倍数包括0,7,14,... 对于p=5,可能的余数是0,1,2,3,4。其中只有0是7的倍数。所以失败条件(余数是7的倍数)只是0。所以对于所有素数,失败条件是余数等于0?不,对于p≥11,余数7是可能的,并且7是7的倍数,所以也要失败。对于p≤7,余数7不可能出现,所以失败条件只有0。正确!所以对于所有p,Fail_p = {0} 当 p ≤ 7;{0, 7} 当 p ≥ 11。 注意 p=7 时,7 mod 7 = 0,所以 {0}。这与我们之前分析一致。

合并计算:
我们有 M, r, 新素数 p, 新余数 a。
我们要找 r_new 满足:
r_new % M == r
r_new % p == a
因为 M 和 p 互质,使用扩展欧几里得求 M 模 p 的逆元 t。
我们不需要完全扩展欧几里得,因为 p 很小(最大53),我们可以简单地尝试 t 从 1 到 p-1,找到 (t * M) % p == 1。由于这一步在每个节点都会做,但节点总数不多,所以暴力找逆元即可。
然后 x = (a - r) % p; 如果 x < 0 则 x += p;
delta = (x * t) % p;
r_new = r + M * delta;
M_new = M * p;

但注意:如果 M_new 可能超出 long long 范围?我们只在 M <= N 的情况下才相乘。此时 M <= 10^17, p <= 53, 乘积 <= 5.3e18 < 9.22e18,所以 long long 安全。为了安全我们可以使用 unsigned long long 但需要小心减法。我们将使用 long long,保证非负。

计数函数 count_n(r, M, N):
计算在 [1, N] 中满足 n ≡ r (mod M) 的数量。
如果 r == 0:
return N / M; // 因为 n = M, 2M, ... <= N
否则 (r > 0):
if (r > N) return 0;
else return (N - r) / M + 1;

注意 r 的范围:我们应当保证 0 <= r < M。在我们的合并中,r_new 计算后可以取模 M_new,使其在 [0, M_new) 内。

现在我们有计算 A_k 的方法。对于每个 k,我们调用 dfs(0, k, 1, 0, N, count)。初始 M=1, r=0 代表空条件(满足所有数的条件)。然后返回 count 即 A_k。

验证 U(1470) = 4293 已知。我们可以先测试 N=1470。

在编写代码之前,我们验证公式 U(N) = 2N + sum_{k=1} (p_{k+1} - p_k) * A_k,其中 A_k 是前k个素数失败条件的计数。注意 A_0 = N。但我们用公式 U = 2N + sum_{k=1} (p_{k+1}-p_k)A_k,这里 k 从 1 开始。求和到最后一个非零 A_k。之后我们还需确认 A_k 的计算是否正确。

让我们手工计算小 N 或编写测试逻辑。或者我们直接写代码并验证小 N。

另外注意:我们在递归中,对于 M > N 的情况,我们检查 r 是否满足余下条件。这意味着我们需要余下素数的 Fail 集。如果全部满足,count += 1。这正确,因为此时 M > N 且 0 < r <= N,只有一个候选 r。我们必须确保 r 对于之前所有条件已经满足,对于后续条件也满足。这符合逻辑。

现在我们需要确定 k 的上限。我们可以循环 k = 1,2,3,... 每次计算 A_k。如果 A_k == 0 则停止。我们需要 primes 数组至少到 k+1(因为需要 p_{k+1})。我们可以预生成足够的素数,比如 100 以内。

现在总结算法:

生成素数向量 primes,至少包含前20个素数。

读入 N = 10^17。

初始化 U = 2 * N。

对于 k = 1, 2, 3, ...:
a. 计算 A_k = count_fail(k, N)。
b. 如果 A_k == 0: 跳出循环。
c. 令 p_k = primes[k-1]? 注意索引:primes[0] 是第一个素数2。对于 k=1,p_1 = primes[0] = 2; p_{k+1} = primes[1] = 3。我们需要差值 p_{k+1} - p_k。
d. U += (primes[k] - primes[k-1]) * A_k。 注意 primes 索引:k 从1开始,我们需要 primes[k] - primes[k-1]。

输出 U。

我们需要确保 count_fail 正确实现。我们还需要注意递归可能较深,但 k 最大约 15-16,递归深度不超过16,无妨。

实现细节:

使用 long long 类型。

扩展欧几里得或暴力逆元:由于 p 小,写一个函数 mod_inverse(M_mod, p) 返回 t 使得 (t * M_mod) % p == 1。

小心整数溢出:计算 r + M * delta 时,M * delta 可能达到 M * (p-1) <= (1e17)*53 = 5.3e18 < 9e18,安全。但为了防止意外,可以使用 unsigned long long 或者 __int128。我们用 long long 足够,只要确保非负且不溢出。实际上 N 是 10^17,M 在 M <= N 条件下最大 10^17,p 最大 53,delta 最大 52,乘积 5.2e18,小于 2^63-1 ≈ 9.22e18。安全。

在 count_n 中,使用 (N - r) / M,如果 N < r 则为负数,需判断。

我们来测试 N=1470 的情况。
已知 U(1470) = 4293。
我会尝试手动模拟或者编写代码后再验证。但此处只能提供代码和思路。所以我们需要确保代码正确。

编写代码时,我们需注意 Fail 集合:对于 p <= 7 只有 {0};对于 p >= 11 有 {0, 7}。但 p=7 时是 {0},不是 {0,7}。

现在编写代码。

但在继续之前,让我们再思考是否遗漏了什么。
条件:对于 p>=11,失败条件为 n%p == 0 或 n%p == 7。因为 7 是 7 的倍数。14 也是,但余数范围 0..p-1,对于 p>=11,14 可能吗?对于 p=13,14 超出范围 0..12,所以只有 0 和 7。对于 p=17,0 和 7。对于 p=7,余数 0..6,只有 0 是 7 的倍数。因此正确。

有没有可能对于 p=7 余数也是7的倍数,但余数等于0是唯一。没问题。

那 u(n) 对于某些 n 是否可能不存在?即对于所有素数 p,n%p ∈ Fail_p。这样的 n 存在吗?n=0 对所有 p 满足,但 n 从 1 开始。是否存在正整数使得对所有素数 p 都满足失败条件?这意味着 n 是 2,3,5,7 的倍数(即 210 的倍数),且对于所有素数 p≥11,n%p ∈ {0,7}。这相当于 n ≡ 0 或 7 (mod p) 对于无限多个 p。由中国剩余定理,这样的 n 是否存在?这要求对于任何有限素数集,都存在满足条件的解。但是对所有无限多个素数的条件可能没有正整数解(除了0)。实际上,对于任何正整数 n,一定存在素数 p 使得 n%p 不是 0 或 7(由于素数无限,且 n 的因子有限,以及模条件)。所以 u(n) 对于所有正整数 n 都有定义。因此 U(N) 对所有 N 有效。我们的算法会覆盖所有 n,累计 count 总和应等于 N。我们可以加一个断言:sum A_{k-1} - A_k = N,或者直接验证所有 A_k 的累计和等于 N?由于公式 U = sum p C(p),而 sum C(p) = N。我们可以通过验证 sum_{k} (A_{k-1} - A_k) = N 来检测正确性。其中 A_0 = N。这可以作为一个调试检查。

在我们的算法中,我们并没有直接计算 C(p),而是用求和公式。我们可以选择直接计算 C(p) = A_{k-1} - A_k,并 sum p*C(p),这样更容易验证。实际上两者等价。我们可以计算 A_{k-1} 和 A_k 然后相减。但需要注意 A_0 = N。我们可以维护 prev_A = N,然后对于 k=1.. 计算 A_k,然后 C = prev_A - A_k; U += primes[k-1] * C; prev_A = A_k; 直到 A_k == 0。这避免了需要 primes[k] - primes[k-1] 的公式,且更直接验证 sum C = N。两种方法都可以,但后者更直观。我们就用后者。

算法改写:
U = 0
prev_A = N
primes = [2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53,59,61,67,71,73,79,83,89,97]
for k = 1 to primes.size():
A_k = count_fail(k, N) // 前k个素数的失败条件
C = prev_A - A_k
U += primes[k-1] * C
prev_A = A_k
if A_k == 0: break

最后输出 U。

检查:对于 N=1470,我们预计算应得到 4293。

现在我们实现 count_fail(k, N):
使用全局 primes 向量。
写一个递归 lambda 或函数。
由于需要访问 Fail 集,我们写一个内联函数 get_fail(p) 返回 vector<int>。

cpp
vector<int> get_fail(int p) {
if (p <= 7) return {0};
else return {0, 7};
}
递归函数:
我们传递 idx, k, M, r, N, 并通过引用传递 count。
为了加速,可以使用迭代或递归。由于最大深度15,递归简单。

cpp
void dfs(int idx, int k, long long M, long long r, long long N, long long &ans) {
if (idx == k) {
ans += count_n(r, M, N);
return;
}
int p = primes[idx];
if (M > N) {
if (r == 0 || r > N) return;
// 检查 r 是否满足 p 到 primes[k-1] 的条件
for (int j = idx; j < k; ++j) {
int q = primes[j];
int rem = r % q;
bool ok = false;
for (int a : get_fail(q)) {
if (rem == a) { ok = true; break; }
}
if (!ok) return;
}
ans += 1;
return;
}
// M <= N
// 计算 M 模 p 的逆元
long long inv = mod_inverse(M % p, p); // M % p 一定非0因为M与p互质
for (int a : get_fail(p)) {
long long diff = (a - r) % p;
if (diff < 0) diff += p;
long long delta = (diff * inv) % p;
long long r_new = r + M * delta;
long long M_new = M * p;
// 规范化 r_new 到 [0, M_new)
r_new %= M_new;
dfs(idx+1, k, M_new, r_new, N, ans);
}
}
mod_inverse: 由于 p 是素数且 M%p != 0,可以计算逆元。使用扩展欧几里得或费马小定理。p很小,循环即可。

cpp
long long mod_inverse(long long a, int p) {
// a 在 [0, p-1] 范围,p 是素数
for (long long t = 1; t < p; ++t) {
if ((a * t) % p == 1) return t;
}
return 1; // 不应该到达
}
count_n:

cpp
long long count_n(long long r, long long M, long long N) {
if (r == 0) return N / M;
if (r > N) return 0;
return (N - r) / M + 1;
}
但是等等:在 M > N 分支中,我们检查了 r 对于余下素数,如果全满足则 ans += 1。这个逻辑对吗?场景是:当前我们已经合并了前 idx 个素数的条件,得到模 M 和余数 r,且 M > N。这意味着满足前 idx 个条件的数在 [1, N] 中至多只有 r(若 0 < r <= N)。如果 r 不在范围内,则 0 个。如果 r 在范围内,那么候选只有 r。我们需要这个候选同时满足剩余的素数条件。所以我们直接检查 r % q 是否属于 Fail_q。这对吗?是的,因为如果 r 满足所有条件,那么它就是满足全部条件的唯一 n ≤ N,所以贡献为 1。这里隐含了我们要求 r 必须满足后续条件,而不仅仅是被动接受。这是正确的,因为我们正在为前 k 个素数条件寻找所有满足条件的 n。

但是有一个微妙之处:初始 M=1, r=0 代表恒等条件。那么 M=1 <= N,所以会进入正常分支。在递归过程中,当我们逐步增加素数,M 会变大。如果某一步 M 超过 N,我们进入剪枝分支,此时我们假设所有前 idx 条件已经被 r 满足(确实如此),那么我们检查后续条件。这等效于在完整的模 M_total 下只有一个候选 r 满足所有条件。因为后续条件的加入不会改变 r 的值(因为 M 已经足够大,且新模数是 M * q > N,解仍然是 r 如果满足条件的话)。所以这是正确的。

但需要注意:当我们合并新条件时,我们计算了 r_new 和 M_new,并且规范化 r_new %= M_new。在 M_new 可能仍然 <= N 的情况下,我们继续递归。如果 M_new > N,我们进入剪枝。这里有个问题:当我们在某一步乘法后 M_new > N,此时 r_new 是已经满足当前新条件的唯一候选。我们需要检查后续条件。这与我们剪枝分支的逻辑一致。

还有一个边界情况:r_new 可能为 0。如果 r_new == 0,意味着候选 n 是 M_new 的倍数。由于 M_new > N,最小正倍数是 M_new > N,所以没有 ≤ N 的解。但在剪枝分支中我们有 if (r == 0 || r > N) return; 这正确排除了 r=0。所以没问题。

测试:我们可以用较小 N 手动计算。但我们先信任逻辑,写完代码后我们可以用给出的暴力数据的一部分来验证。比如 N=210 等,我们可以计算 U(210) 并与暴力数据对照。提供的暴力数据显示了前一些 n 的 u(n) 值。我们可以写一个验证脚本,但我们目前只能靠大脑模拟或推理。我们也可以直接用 C++ 编译运行心理模拟?我无法运行代码,但可以尝试手动追踪小 N 或者提供代码。

不过可能有一个陷阱:u(n) 是最小的素数 p 使得 n%p 不是7的倍数。也就是说,我们需要第一个“成功”的素数。我们的失败集合正是“余数是7的倍数”。对于 p=2,3,5,7,失败条件是 {0}。对于 p>=11,失败条件是 {0,7}。这没错。

但是否存在某些 n 使得对于所有素数 p <= P_max 都失败,然后对于更大的素数成功?这就是我们的 A_k 逻辑。

还有一点:我们是否需要考虑 p 可能等于 7 的情况?对于 p=7,失败集是 {0},7本身是素数。u(n) 可以是 7 吗?如果 n 满足 n%2=0, n%3=0, n%5=0, n%7≠0,那么 u(n)=7。这是可能的。我们的公式中 primes 包含 7,所以 C(7) 会被计算。

验证 u(210) 从提供的数据:数据中最后一条 210 11。我们来算:210是2,3,5,7的倍数。%2=0 (失败), %3=0, %5=0, %7=0, %11=210%11=1 (不是0或7) -> 成功,所以 u(210)=11。这与数据吻合。
根据我们的算法:A_0=210. A_1 = #{偶数}=105. A_2 = #{6的倍数}=35. A_3 = #{30倍数}=7. A_4 = #{210倍数}=1. A_5 = #{满足2,3,5,7失败且11失败}。对于11失败意味着 n%11 ∈ {0,7}。210%11=1,所以210不满足11失败。可能没有 ≤210 的数满足前5个素数失败?我们看满足前4个失败的是210的倍数:210,420,... <=210只有210。210%11=1不在{0,7},所以 A_5 = 0。则 C(2)=A0-A1=105; C(3)=35-7=28? 等等我们来计算 U(210):
C(2)=105 -> 2*105=210
C(3)=A1-A2=105-35=70 -> 3*70=210
C(5)=A2-A3=35-7=28 -> 5*28=140
C(7)=A3-A4=7-1=6 -> 7*6=42
C(11)=A4-A5=1-0=1 -> 11*1=11
总和 U = 210+210+140+42+11 = 613。我们没有暴力数据验证 U(210),但可以相信逻辑。提供的 U(1470)=4293,我们可以用此验证代码。

还有一点:素数的顺序是否严格按照升序?是的,u(n) 找最小的素数,所以我们必须按照素数大小升序检查。我们的 primes 数组是升序的。

现在考虑 N=10^17 时,long long 是否够用?U 的总和约等于 N 乘以平均 u(n)。对于偶数,u(n) 平均可能略大于2。大致在2到几十之间。所以 U 约在 2e17 到 1e18 之间,小于 9e18,所以 long long 够用。

但中间 M 可能超过 64 位吗?我们在 M > N 时剪枝不继续乘法。M 最大只会在最后一次乘法时 <= N,乘完可能大于 N 但不超出 long long。我们来分析最坏情况:当 M 接近 N 时乘以一个素数,假设 N=10^17,前一个 M 可能小于或等于 10^17。素数最大我们可能用到 53。乘积最大为 10^17 * 53 = 5.3e18 < 9.22e18。所以安全。我们是否需要用到比 53 更大的素数?当 k 继续增加,A_k 已经为 0,循环终止。所以乘法极限就是 10^17 乘以下一个素数。但注意如果 M 在乘以某个素数后略大于 N,但可能未超过 9e18,仍安全。如果我们计算更大的 k,A_k=0 我们已经 break,不会执行 dfs。所以没问题。

现在,剪枝分支中 M > N 的情况下,我们直接检查 r 对后续素数。此时 r 是某个小于等于 N 的数。后续素数可能很大,比如 59, 61。我们只是做模运算 r % q,这在 64 位内完全没问题。

现在需要确保 DFS 中的 M 和 r 变量类型为 long long。为了安全,可以使用 unsigned long long 但需要处理减法。我将使用 signed long long,并确保 r 在 [0, M-1] 范围内。

关于 count_n 函数:对于 r > 0,使用 (N - r) / M + 1。如果 N < r,则为负数除法,所以先判断 if (r > N) return 0;。

还有一个潜在问题:在 M > N 分支中,我们检查了 r 是否满足后续条件,如果满足则 ans += 1。但是否存在 r 满足后续条件但 r 不是最终模数下的唯一解?因为 M > N,且最终的模数 M_total >= M > N,所以任何解 x 必须满足 x ≡ r (mod M)。在 [1, N] 范围内,满足 x ≡ r (mod M) 的唯一可能就是 x = r(如果 r ∈ [1, N])。这是正确的。但是否有可能会因为后续条件改变这个解?如果 r 满足后续条件,那么它就是解。如果 r 不满足,就没有解。正确。

还有一个边界:初始条件 M=1, r=0,这表示我们还没有强加任何条件。这是正确的。

我们还需要确认对于 k=0 的处理?我们的循环从 k=1 开始,对应素数 2。A_0 = N。我们使用 prev_A = N。没问题。

现在代码框架:

cpp
#include <iostream>
#include <vector>
#include <cstdint> // 或者直接使用 long long
using namespace std;

using ll = long long;

vector<int> primes = {2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53,59,61,67,71,73,79,83,89,97};

vector<int> get_fail(int p) {
if (p <= 7) return {0};
else return {0, 7};
}

ll mod_inverse(ll a, int p) {
a %= p;
for (ll t = 1; t < p; ++t) {
if ((a * t) % p == 1) return t;
}
return 1;
}

ll count_n(ll r, ll M, ll N) {
if (r == 0) return N / M;
if (r > N) return 0;
return (N - r) / M + 1;
}

void dfs(int idx, int k, ll M, ll r, ll N, ll &ans) {
if (idx == k) {
ans += count_n(r, M, N);
return;
}
int p = primes[idx];
if (M > N) {
if (r == 0 || r > N) return;
for (int j = idx; j < k; ++j) {
int q = primes[j];
int rem = r % q;
bool ok = false;
for (int a : get_fail(q)) {
if (rem == a) { ok = true; break; }
}
if (!ok) return;
}
ans += 1;
return;
}
ll inv = mod_inverse(M % p, p);
for (int a : get_fail(p)) {
ll diff = (a - (r % p)) % p;
if (diff < 0) diff += p;
ll delta = (diff * inv) % p;
ll r_new = r + M * delta;
ll M_new = M * p;
r_new %= M_new;
dfs(idx+1, k, M_new, r_new, N, ans);
}
}

ll count_fail(int k, ll N) {
ll ans = 0;
dfs(0, k, 1, 0, N, ans);
return ans;
}

int main() {
ll N = 100000000000000000LL; // 10^17
// 可以先用小N测试
// N = 1470;
ll U = 0;
ll prev_A = N;
for (int k = 1; k <= (int)primes.size(); ++k) {
ll A_k = count_fail(k, N);
ll C = prev_A - A_k;
U += primes[k-1] * C;
cout << "k=" << k << " p=" << primes[k-1] << " A_k=" << A_k << " C=" << C << " U=" << U << endl; // debug
prev_A = A_k;
if (A_k == 0) break;
if (k == (int)primes.size()) {
// 需要更多素数,但理论上不会
cerr << "Need more primes!" << endl;
}
}
cout << "Final U = " << U << endl;
return 0;
}
等等,我们在 dfs 中使用 count_n 应该是在 idx == k 时调用,但 count_n 期望 r 满足 0 <= r < M。在 M > N 分支中,我们直接加1,这相当于 count_n 返回1,因为 r != 0 且 r <= N 且 M > N 时 count_n(r, M, N) 返回1(如果 r>0)。实际上我们可以直接用 count_n 来统一:在 M > N 分支中,如果我们确定 r 满足所有后续条件,我们可以调用 ans += count_n(r, M, N)。但此时 M 是当前的模数(可能不是最终完整模数),而 r 已经满足所有后续条件,最终完整模数是 M * q_{idx} * ... ,但因为 M 已经 > N,count_n(r, M, N) 会返回 0 或 1。由于最终模数更大,count_n 用更大的模数会返回相同的结果(因为 M > N 时,无论乘多少,只要 r 不变,符合条件的 n 都是 r 如果 r 在范围内)。所以我们可以直接使用 ans += count_n(r, M, N) 而不用手动加1,这样更统一。但需注意如果后续有条件失败,我们直接 return。这没问题。或者我们保留手动检查后续条件并加1的方式,更不依赖 M。都可以。

不过有个微妙点:在 M > N 分支中,当前 r 是在当前 M 下的余数,且满足前 idx 条件。如果我们检查后续条件全部满足,则最终解确实是 r(因为 r 满足所有条件)。所以 ans += 1 正确。如果我们改用 count_n(r, M, N) 而不再乘以后续素数,会得到同样结果,因为 M > N 且 r>0,返回1。所以我保留显式检查以便清晰。

还需要注意:在 M > N 剪枝分支中,我们假设了当前 M 已经大于 N,意味着在当前的模数下,解唯一。然后我们检查该解是否满足后续条件。但是否存在这种情况:当前 M > N,但 r=0,此时 count_n 返回 0。正确。如果 r 在范围内但不满足后续条件,返回0。正确。

但是否需要考虑:当前 r 满足条件,但最终的模数 M_total 可能使得 r 不是最终模数下的正确余数?因为我们在剪枝时并没有使用后续素数来更新 r 和 M,而是直接检查 r 是否满足后续条件。如果 r 满足后续条件,那么 r ≡ a (mod q) 对于后续 q 成立。那么在完整模数下,满足所有条件的解就是那些 x ≡ r (mod M) 且满足后续条件的 x。由于 M > N,在 [1,N] 内至多只有一个 x ≡ r (mod M),即 r 本身。如果 r 满足后续条件,那么它是解。如果 r 不满足,无解。所以逻辑无误。

现在还有一个问题:当 M <= N 时,我们使用乘法 M_new = M * p。如果 M * p 可能溢出 long long?之前分析最大 5.3e18 < 9e18,安全。

验证 N=1470 时运行情况:
我们可以模拟一下,但最好实际测试。由于无法执行,我们信任代码。

边缘情况:对于 p=2,3,5,7,Fail 集只有 {0}。对于 p=7,我们代码 if (p <= 7) return {0},所以 p=7 也是 {0}。正确。

现在复杂度:k 最大约为 15 左右。每个 k 的 dfs 访问节点数等于所有组合数,但随着剪枝(M > N)实际节点数远小于 2^{k}。最大 k=15 时组合数 2^{11}=2048,所以很快。完全可以瞬间算出。

现在我们确认 U(10^{17}) 的值。我们需要输出结果。但作为回答,我们需要提供 C++ 代码以及可能的结果?问题是“给出cpp代码”,所以主要提供代码。但通常 Project Euler 问题可能要求输出答案。我们可以给出代码,并说明结果(如果我们能运行的话)。由于我是 AI,我无法实际编译运行,但可以基于逻辑推理出代码的正确性,并提供代码。用户可能期望得到可计算 U(10^17) 的 C++ 代码。我应该输出完整的 C++ 程序。

但为了确保正确性,再检查一下 sum 的公式推导是否有误。

我们有 u(n) 定义。设 S 为所有 n 的集合。我们按照第一个成功的素数 p 分类:
C(p) = #{n : u(n)=p}.
U = Σ p C(p).

设 A_k 为满足前 k 个素数都失败的 n 的数量。A_0 = N。
C(p_k) = A_{k-1} - A_k.
U = Σ p_k (A_{k-1} - A_k).
这无疑是正确的,因为我们按照最小素数分类,前 k-1 个失败且第 k 个成功。

我们的计算 C(p_k) 的方法:A_{k-1} = count_fail(k-1, N). A_k = count_fail(k, N). C = A_{k-1} - A_k.

现在,count_fail(k) 实现的是前 k 个素数条件都失败的 n 的数量。索引从 1 到 k 对应前 k 个素数。实现正确。

但是否有素数被遗漏?比如 p=7 后面是 p=11。我们是否包含了所有素数?我们的 primes 数组是按序的。只要 primes 覆盖所有可能作为 u(n) 的素数,且最终的 A_K 变为 0,那么求和就完整。由于 A_K 变为 0 意味着没有 n 需要更大的素数了。

现在还有一个小问题:u(n) 定义“最小的使得 n%p 不是7的倍数的素数 p”。那么对于 p=2,n%2 != 0 即为成功。对于偶数,n%2 == 0 失败。这符合我们的 Fail={0}。对于 p=11,失败是 {0,7}。有没有可能 p=3 时失败集是 {0},但 7 的倍数包括 7,由于 7 > 3,所以没有 7,所以失败只有 0。这都对。

但有没有可能 n%p 是 14, 21 等?对于 p 足够大,比如 p=17,余数范围 0..16,7的倍数有 0,7,14。我们失败集只包含了 {0,7},漏了 14!这是一个严重疏忽!

让我们重新阅读定义:“使得n除以p的余数(即n%p)不是7的倍数的素数p”。
7的倍数:0, 7, 14, 21, 28, ...
对于给定的 p,可能的余数范围是 0 到 p-1。在这个范围内,7的倍数有哪些?
如果 p <= 7:余数最大为 p-1 <= 6,所以7的倍数只有 0。
如果 p = 11:余数 0..10,7的倍数有 0, 7。所以失败 {0,7}。
如果 p = 13:余数 0..12,7的倍数有 0, 7。14 > 12,所以 {0,7}。
如果 p = 17:余数 0..16,7的倍数有 0, 7, 14。所以失败集应为 {0, 7, 14}!
哎呀!我之前错误地认为只有 0 和 7,但 14 也是 7 的倍数!只要 p > 14,就会包括 14。
p=19:0,7,14。
p=23:0,7,14,21。
p=29:0,7,14,21,28。
以此类推。

这是一个重大遗漏!我们需要修正 Fail 集合!
Fail_p 集合应该包含所有 0 ≤ k7 < p 的 k7。
即 Fail_p = { k7 | k >= 0, k7 < p }。

重新分析:
对于 p=2: Fail = {0}
p=3: {0}
p=5: {0}
p=7: {0} (因为 7 < 7? 0*7=0, 1*7=7 不小于 7,等于 7 但余数范围 0..6,所以没有 7。)
p=11: {0, 7}
p=13: {0, 7}
p=17: {0, 7, 14}
p=19: {0, 7, 14}
p=23: {0, 7, 14, 21}
p=29: {0, 7, 14, 21, 28}
p=31: {0, 7, 14, 21, 28}
p=37: {0, 7, 14, 21, 28, 35}
一般地,Fail_p = {0, 7, 14, ..., floor((p-1)/7)*7}。集合大小为 floor((p-1)/7) + 1。

这极大改变了计数!对于每个 p >= 11,失败余数不仅是 2 个,而是大约 p/7 个。所以随着 p 增大,失败概率增加,也就是说第一个成功的素数 p 会更早出现?不,失败条件变多了,意味着 n 更容易在较小的素数失败,因此需要尝试更大的素数才能成功。所以 u(n) 的平均值会更大?让我们重新思考:我们要找最小的 p 使得 n%p 不是 7 的倍数。如果失败条件变多(即更多的余数被认为是“7的倍数”),那么 n%p 更容易是 7 的倍数,也就是更容易失败。因此需要尝试更多的素数才能找到成功的 p。所以 u(n) 会变大。

我们回顾例子:
u(14) = 3。 14%2=0 (是7倍数),失败;14%3=2 (不是7倍数),成功 -> 3。符合。
u(147) = 2。147%2=1 (不是7倍数) -> 2。符合。
u(1470) = 13。1470: %2=0, %3=0, %5=0, %7=0, %11=7 (是7倍数), %13=1 (不是) -> 13。这里 11 失败是因为余数 7 是7倍数。13 成功因为余数 1 不是。这符合我们的新 Fail 集,因为 13 的 Fail 是 {0,7},1 不在其中。

如果我们之前遗漏了 14 等,那么对于更大的 p,Fail 集更大。因此对于 N=10^17,A_k 的衰减可能更慢?因为失败条件更宽松,满足前 k 个素数都失败的 n 的数量可能更多。这意味着我们需要更多的素数才能让 A_k 变为 0。我们需要重新评估最大素数。

首先,我们看看对于之前的例子 1470,我们的遗漏是否会影响 A_k 的计算?1470 很小,最大素数只到 13。对于 p<=13,Fail 集是 {0} (p<=7) 和 {0,7} (11,13)。14 还没有出现在 Fail 集中,因为 p<17。所以前面的分析对于小 N 仍是正确的。我们之前用 1470 验证时不会发现这个错误。但 N=10^17 可能涉及 p >= 17,所以必须修正。

现在我们重新设计 Fail 集合。
对于每个素数 p:
如果 p <= 7: Fail = {0}
如果 p > 7: Fail = { 7 * k | k = 0, 1, ..., floor((p-1)/7) }。
即大小为 f(p) = floor((p-1)/7) + 1。

注意当 p=11 时,(11-1)/7 = 10/7 = 1,所以 k=0,1 -> {0,7}。符合。
p=13: 12/7=1 -> {0,7}。
p=17: 16/7=2 -> {0,7,14}。
p=19: 18/7=2 -> {0,7,14}。
p=23: 22/7=3 -> {0,7,14,21}。
p=29: 28/7=4 -> {0,7,14,21,28}。
等等。

这个改变导致我们的递归中,每个 p 的分支数量变多。之前对于 p>=11 只有 2 个分支,现在有约 p/7 个分支。例如 p=47 时,floor(46/7)=6,有 7 个分支。组合数会爆炸吗?
我们需要计算所有 A_k,k 最多可能到多少?现在失败条件更多,可能导致 A_k 衰减更慢,即需要更多素数才能使 A_k 变为 0。但 N=10^17 有限,我们需要素数到多大?最坏情况下,满足所有失败条件的 n 是“对于所有素数 p <= P,n%p 是 7 的倍数”。这相当于 n ≡ 0 mod 2,3,5,7;且 n ≡ 0,7,14,... mod p 对于更大的 p。由于模数乘积增长非常快,即使每个模数有多个允许余数,在给定的 N 下,满足所有条件的 n 可能仍然很少,并且组合总数是所有 f(p) 的乘积。如果素数太多,乘积可能爆炸。我们必须评估最大素数和总组合数,确保算法可行。

让我们估计:
对于每个素数 p,失败条件的“密度”大约是 f(p)/p ≈ 1/7。因为 f(p) ≈ p/7。
因此,满足所有 p <= P 失败条件的数的密度大约是 ∏ (f(p)/p) ≈ (1/7)^{π(P)-4} * (1/2)*(1/3)*(1/5)*(1/7) 等等?具体地,
模 M = ∏ p,符合条件的剩余类总数 R = ∏ f(p)。密度 R/M = ∏ (f(p)/p)。
对于 p=2,3,5,7: f(p)=1,密度 = 1/p。
对于 p>=11: f(p)/p ≈ 1/7。
所以整体密度大约为 (1/210) * (1/7)^{k-4},其中 k 是素数个数。
N = 10^17。符合条件的数的期望数量约为 N * 密度。
当密度 * N < 1 时,A_k 可能变为 0 或很小。
N * (1/210) * (1/7)^{k-4} ≈ 1 => (1/7)^{k-4} ≈ 210 / 10^17 = 2.1e-15
k-4 = log(2.1e-15) / log(1/7) ≈ -14.7 / -1.946 ≈ 7.55。
所以 k-4 ≈ 8,即 k ≈ 12。也就是大约到第12个素数左右,A_k 变为 0。让我们精确一下:
素数序列:
1: 2 (f=1)
2: 3 (1)
3: 5 (1)
4: 7 (1)
5: 11 (f=2)
6: 13 (2)
7: 17 (3)
8: 19 (3)
9: 23 (4)
10: 29 (5)
11: 31 (5)
12: 37 (6)
13: 41 (6)
14: 43 (7)
15: 47 (7)
16: 53 (8)
17: 59 (9)
18: 61 (9)
19: 67 (10)
20: 71 (11)
...

如果 k=12 (素数37): R = 1*1*1*1 * 2*2*3*3*4*5*5*6 = 1 * (22)(33)(4)(55)*6 = 4 * 9 * 4 * 25 * 6 = 4*9=36; 36*4=144; 144*25=3600; 3600*6=21600。组合数 R = 21600。这个组合数我们完全可以用 DFS 枚举(21600 个叶子节点,没问题)。
k=13 (41): R = 21600 * 6 = 129600。
k=14 (43): R = 129600 * 7 = 907200。
k=15 (47): R = 907200 * 7 = 6,350,400。
k=16 (53): R ≈ 6.35e6 * 8 ≈ 50,803,200。
k=17 (59): R ≈ 5e7 * 9 ≈ 4.5e8。
...

可见组合数随着 k 增长非常快。但我们必须算到 A_k = 0 为止。根据期望,A_k 在 k≈12 或 13 时变得很小(可能为0或正数)。我们需要准确计算 A_k,一旦 A_k = 0 就停止。根据密度估计,M 在 k=12 时多大?
k=11 (31): M = 2*3*5*7*11*13*17*19*23*29*31 = 200,560,490,130 ≈ 2e11。
k=12 (37): M = 7.4e12。
k=13 (41): M = 3.0e14。
k=14 (43): M = 1.3e16。
k=15 (47): M = 6.1e17。
k=16 (53): M = 3.2e19 > 10^17。

因为 M 是模数,M 在 k=15 时达到 6e17,k=16 时 3.2e19 > N。当 M > N 时,每个符合条件的剩余类 r 如果 ≤ N 则贡献 1。A_k 的数量等于满足条件的 r 的个数(且 r ≤ N,对于 r=0 需 M ≤ N 否则为 0)。注意 r 是在 [0, M) 内均匀分布?实际上条件剩余类 R 是已知的。当 M 很大时,A_k 就是 R 中 ≤ N 的元素个数。由于 M > N,最多 N+1 个可能(但 R 大小可能很大,不过我们只关心 ≤ N 的)。计算 A_k 需要统计 R 中有多少落在 [1, N] 内。这正是我们 DFS 剪枝所做的事情。

我们能否在 R 很大的情况下高效计算 A_k?如果 k 较大,M 很快超过 N,我们的 DFS 剪枝(M > N 分支)会极大地减少计算量。因为在 M > N 后,我们不再继续乘以下一个素数,而是直接检查当前的 r 是否满足后续条件。这意味着对于大的 k,递归深度会限制,大部分组合会被剪枝掉。

让我们分析剪枝的效率:当我们进行 DFS 时,我们逐步合并模数。一旦当前的 M > N,我们就停止扩展并检查后续条件。对于最终的 k,我们实际展开的树只包括 M <= N 的节点,以及 M > N 后不再扩展。因此实际访问的节点数大约等于所有在中间步骤中 M <= N 的可能组合,加上 M 首次 > N 时的节点。

因为我们按顺序加入素数,M 是递增的。M 会在某一步超过 N。对于 N=10^17,M 在哪个索引超过 N?
我们之前计算:k=14 时 M≈1.3e16 < 10^17;k=15 时 M≈6.1e17 > 10^17。所以对于 k<=14,M <= N;对于 k>=15,在合并第15个素数(47)时 M 会超过 N。
因此:

对于 k <= 14,DFS 会完全展开所有组合,因为 M 始终 <= N。所以我们需要枚举所有组合数。

对于 k = 14,组合数 R_14 = 前14个素数的 f(p) 乘积。
让我们计算前14个素数:
2:1
3:1
5:1
7:1
11:2
13:2
17:3
19:3
23:4
29:5
31:5
37:6
41:6
43:7
乘积 = 1*1*1*1 * 2*2*3*3*4*5*5*6*6*7 = 4 * 9 * 4 * 25 * 36 * 7 = 4*9=36; 36*4=144; 144*25=3600; 3600*36=129600; 129600*7=907200。
所以 A_{14} 需要枚举 907,200 个组合。每个组合做 CR,计算 r,然后 count_n。907,200 对于 C++ 是完全可以的(不到百万)。计算每个组合需要常数时间,总时间小于 0.1 秒。

对于 k = 15,我们需要计算 A_{15}。理论上组合数 R_15 = 907,200 * 7 = 6,350,400。但是此时 M 会超过 N。我们在 DFS 过程中,在 idx=14(第15个素数,索引从0开始)时,M_14 可能是 1.3e16 <= N,然后乘以 47 得到 M_15 > N。在进入 idx=15 的节点时,M > N,我们会进入剪枝分支,不再继续递归。所以对于 k=15,我们实际上需要遍历 R_14 = 907,200 个节点(在 idx=14 时),然后对于每个节点,有 7 个分支进入 idx=15(此时 M > N 并检查后续条件)。所以实际访问的节点数大约是 907,200 * 7 ≈ 6.3e6 个叶子节点的检查,加上中间节点,总共约 7 百万次操作。这仍然非常可行,几毫秒到几十毫秒。

对于 k=16,A_{16} 的组合数 R_16 = 6.35e6 * 8 = 50.8e6。但我们不需要完全枚举所有组合!因为对于 k=16,我们在 idx=14(第15个素数)时 M 已经超过 N?等等:k 表示总共的素数个数。对于 k=16,我们要处理前16个素数:素数到53。
在 DFS 过程中,我们逐渐加入素数。当处理到第15个素数(47)时,M 变为 > N。然后我们处理第16个素数(53)时,因为 M 已经 > N,我们会进入剪枝分支,不再乘。这意味着我们在 idx=14(即处理第15个素数)时,M 已经 > N?不,M 是在乘以 47 之后 > N。在合并第15个素数之前,M = M_14 ≈ 1.3e16 <= N。当我们合并 47 后,M 变为 M_15 ≈ 6.1e17 > N。然后我们递归调用 idx=15(对应第16个素数 53)。在这个递归调用开始,我们就检测到 M > N,进入剪枝。所以对于 k=16,我们仍然只需要展开前 14 个素数的所有组合(907,200),然后对每个组合,展开第15个素数的 7 个分支,然后在第16个素数处立即剪枝(检查后续条件)。所以工作量大约是 907,200 * 7 * (检查后续条件的开销)。后续条件检查只是对 r 进行一系列模运算。所以 k=16 工作量约为 6.3e6 量级。

对于更大的 k,比如 k=17,18,...,我们仍然只需要展开到第14个素数的所有组合,以及第15个素数的分支,然后在第16个及以后的素数处全部在剪枝分支中检查条件。剪枝分支中,我们有唯一的候选 r(即满足前15个素数条件的 r),我们需要检查它是否满足从第16个到第 k 个所有素数的条件。在 k 很大时,这只是一系列模运算。所以 A_k 对于 k>=15 的计算都非常快,我们可以在循环中不断增加 k 直到 A_k == 0,即使 k 到很大(比如 100)也很快。

但是等一下:当 k >= 16 时,A_k 的定义是满足前 k 个素数都失败的 n 的数量。由于前 15 个素数已经使得 M > N,满足前 15 个失败条件的 n 已经非常少(最多 R_14 中的那些 r ≤ N 且满足第15个素数条件的数量)。对于每个这样的候选 r,我们需要它同时满足第 16 到第 k 个素数的失败条件。由于后续素数很多,很可能对于大多数 r,很快就会不满足某个素数条件。A_k 会随着 k 增加而递减。我们需要找到 A_k 变为 0 的 k。

关键是我们需要循环 k,并计算 A_k。我们可以在循环中维护一个“候选集合”:所有满足前 k-1 个素数失败条件的 n 的列表?不,我们可以直接每次调用 count_fail(k),由于有剪枝,每次调用 count_fail(k) 会重新从 idx=0 开始递归。对于 k 很大,每次调用都会重复展开前 14 个素数的组合。这会导致重复计算。由于我们需要求很多 A_k(可能几十个),每次 O(1e6) 的重复计算,几十次就是几千万次操作,仍然可以接受(0.几秒)。但我们可以优化:我们可以逐步计算。不过为了简单,直接循环调用 count_fail(k) 即可。C++ 1e7 次简单操作通常在 0.1 秒内。

但我们必须确认我们真的需要 k 达到多大。根据密度,失败概率约 1/7 每个素数。对于候选 r,它通过后续素数失败条件的概率约为 (1/7)^{k-15}。初始候选数量大约为 A_{15}。A_{15} 有多少?根据期望,A_15 ≈ N * density_15。N=1e17, density_15 = (1/210) * (1/7)^{11} ≈ (1/210) * 1 / 1.9e9 ≈ 1 / 4e11。所以 A_15 ≈ 1e17 / 4e11 ≈ 2.5e5。所以大约有 250,000 个候选 r 满足前15个失败条件。然后每增加一个素数,大约乘以 1/7,所以 A_{16} ≈ 35,000,A_{17} ≈ 5,000,A_{18} ≈ 700,A_{19} ≈ 100,A_{20} ≈ 14,A_{21} ≈ 2,A_{22} ≈ 0。所以 k 大概到 22 左右变为 0。也就是说我们最多需要计算到 k ≈ 25。25 次 * 1e6 节点 = 2.5e7 次递归调用。可能稍慢但仍在几秒内,我们可以承受。我们还可以优化:我们可以在一次 DFS 中直接计算所有 k 的 A_k?但不必,简单循环即可。或者我们可以用动态规划:从第15个素数开始,我们有一组候选 r,然后逐步过滤。但由于剪枝已经存在,我们可以写一个循环利用之前的候选集。为了性能和简单,我们可以直接使用循环调用 count_fail(k),如果速度不够,可以优化。我们暂时采用每次重新计算,C++ 速度应该足够。N=10^17,只需计算到 A_k=0。

但注意:之前的组合数 R_14 = 907,200。每次调用 count_fail 都要枚举这 907,200 个组合吗?是的,对于每个 k>=14。这会导致对于 k=14,15,16,... 的每次调用都重复枚举全组合。但 k=14 只有一次,k=15 一次,等等。总调用次数约 10 次。907,200 * 10 ≈ 9e6 个叶子节点。加上内部分支,大约 1e7 次循环,完全没问题。

但是等等,k=14 时 M_14 ≈ 1.3e16 <= N,所以没有剪枝,会完全枚举 907,200 个叶子。对于 k=15,我们枚举 R_14 并在第15层有 7 分支,所以 6.3e6 叶子。对于 k=16,也是 6.3e6 叶子加上剪枝检查。如果我们重复计算 k=15,16,17...,每次都要枚举这 907,200 * 7 个叶子?是的,因为我们从根开始递归。这可能导致总叶子访问量 = 907,200 * 7 * (num_k - 14) ≈ 6.3e6 * 10 = 6.3e7。再加上一些开销,可能接近 1e8 次操作。在 C++ 中 1e8 次简单操作大约 0.5-1 秒。可以接受。但我们可以轻松优化:我们可以将前 14 个素数的所有满足条件的 (M, r) 对缓存下来!因为前 14 个素数的失败条件不依赖于 k。我们可以预先计算所有满足前 14 个素数失败条件的 n 的集合(即 r 和 M),实际上 M 固定为 M_14,r 是所有符合条件的剩余类。然后对于更大的 k,我们只需要在这些 r 的基础上继续应用后续素数条件。这样我们只需要枚举前 14 个组合一次,然后对于每个 k,遍历这 907,200 个 r,并应用从第 15 到第 k 个素数的条件。这样大大减少了计算量。

让我们设计优化方案:
设 P = 前14个素数(2到43)。M_14 = product = 13082761331670030? 我们之前算的是 M_14 ≈ 1.3e16。确切值需要计算。
我们可以先用 DFS(无剪枝)计算所有满足前 14 个素数失败条件的 r 值(0 <= r < M_14),或者只需存储 r 值,因为 M 固定。数量为 907,200。然后 A_{14} = sum count_n(r, M_14, N)。
对于 k > 14,我们需要计算 A_k = 满足前 k 个素数失败条件的 n 数量。
A_k = sum_{r in candidates} [ r 满足第15到第k个素数条件 ] * count_n(r, M_14, N)? 等等!这里有个关键点:对于 k > 14,失败条件包括前14个素数(即 r 条件)加上更多素数(p_15..p_k)。但是完整的模数 M_k = M_14 * p_15 * ... * p_k。满足条件的 n 是那些同时满足 r 条件(mod M_14)和新条件(mod 后续素数)的数。因为 M_14 可能已经接近或大于 N?M_14 ≈ 1.3e16 < 10^17。所以 M_14 <= N。在合并后续素数后,M_k 可能 > N。所以我们需要小心处理:不能简单地将 r 视为最终余数,因为后续素数条件可能会改变 r 和 M。当我们有候选 r (mod M_14) 满足前14个条件,对于每个候选 r,以及后续素数条件,我们需要找到满足 x ≡ r (mod M_14) 且 x ≡ a_j (mod p_j) 的所有 x ≤ N。因为 M_14 <= N,可能有多个 x 满足部分条件。

所以我们需要对每个 r,结合后续素数条件,计算满足条件的 x 的数量。这可以通过对每个 r 递归应用后续素数(从第15个开始)来计算。由于后续素数个数不多(约 5-10 个),我们可以在每个 r 上单独处理。

具体算法:

计算 primes 列表。

计算固定前缀:找到最小的 K0 使得前 K0 个素数的乘积 M > N?或者我们选择一个前缀,使得它的组合数适中,且 M <= N。我们之前分析 K0=14 时 M≈1.3e16 <= N,组合数 907,200。我们可以选 K0=14。也可以选 K0=15? 对于 K0=15,M≈6.1e17 > N,组合数 6.3e6。用 K0=15 前缀会导致 M > N,这意味着对于每个满足前15个条件的 r,它在 [1,N] 内最多有一个实例。因此候选集就是那些 r 值(0<r<=N)本身。这也可以。
哪种更好?如果选 K0=15,M_15 > N,候选 r 的数量约为 A_{15}(大约 2.5e5 个?)。但我们必须先枚举所有组合(6.3e6)来找到这些 r。如果我们选 K0=14,候选组合有 907,200 个,且 M_14 <= N,每个组合可能对应多个 n(因为 N/M_14 ≈ 7.6)。对于后续 k,我们需要对每个组合进行较复杂的计算(因为可能有多个 n)。这可能不如直接在 DFS 中利用剪枝一次完成所有 k?也许最简单的还是我们最初的方案:对每个 k 直接调用 count_fail(k),利用 DFS 剪枝。因为剪枝已经在 M > N 时生效,实际节点数有限。我们最初的复杂度评估显示即使重复计算也是可行的。为了代码简单,我们保留最初的 DFS 方法,每次对 k 调用 count_fail。我们只需修正 Fail 集合。

但最初的 DFS 存在一个问题:当 M > N 时,我们检查了 r 是否满足后续条件,如果满足则计数加1。但现在 Fail 集合变大了(包含 14, 21 等)。我们必须修正 get_fail 函数以返回所有 7 的倍数。

让我们重新实现 get_fail(p):

cpp
vector<int> get_fail(int p) {
vector<int> res;
if (p <= 7) {
res.push_back(0);
} else {
for (int k = 0; 7 * k < p; ++k) {
res.push_back(7 * k);
}
}
return res;
}
现在 mod_inverse 不变。
count_n 不变。

DFS 需要适应 get_fail 返回多个值。

另外,在 M > N 剪枝分支中,我们检查 r 是否满足后续条件:

cpp
for (int j = idx; j < k; ++j) {
int q = primes[j];
int rem = r % q;
bool ok = false;
for (int a : get_fail(q)) {
if (rem == a) { ok = true; break; }
}
if (!ok) return;
}
这没问题。

现在问题是:随着 p 增大,get_fail(p) 的大小可能变得较大(例如 p=97 时大约 13 个值)。在 M <= N 的分支中,分支因子变大。但正如我们分析,M <= N 只在前 14 个素数内成立。第15个素数 47 时 M 变为 > N。所以对于 p=47,当它处于 M <= N 时,我们仍会进入正常分支配?实际上,在合并第15个素数时,M 目前是 M_14 (<=N),我们会进入正常分支,有 f(47)=7 个分支。每个分支产生 M_new > N。然后递归调用下一层时进入剪枝。所以第15个素数的分支是展开的。但后续第16个及以后的素数都在剪枝分支中处理,不再展开分支。因此分支数没有爆炸,仍然是 ~6.3e6 个第15层的节点,可以接受。

我们需要重新评估素数列表需要多大。可能 k 会达到 25 左右。我们需要 primes 数组至少有这么多素数。我们可以生成素数到 200,保证足够。

现在我们可以编写完整代码。

为了进一步优化和确保正确性,我们可以采用缓存前缀组合的方法。这会大大降低重复计算,而且逻辑更清晰:

方案:
设 PREFIX_COUNT = 14 或 15?我们选择 PREFIX_COUNT = 14,因为 M_14 <= N,所有组合都需要用到 count_n 进行计数,后续素数则通过进一步过滤。

但正如前面所说,如果我们缓存了前缀的所有 r (mod M_14),对于每个后续的 k,我们需要计算满足新条件的数量。因为 M_14 <= N,每个 r 代表一个等差数列,其中有多个数。我们不能简单地用 r 是否满足新条件来判断,因为可能有部分等差数列中的数满足新条件,部分不满足。但我们可以对每个 r,结合新条件,计算满足所有条件的 n 的数量。这等价于对每个 r,求解一个新的中国剩余问题:模数 M_14 和新素数们。这本质上和 DFS 没有区别。所以仍然需要类似的计算。

既然简单 DFS 已经足够快,我们就采用简单 DFS。为了防止重复计算,我们可以在主循环中从 k=1 到 K,但是我们可以利用 A_k 的递推:A_{k} 可以从 A_{k-1} 的候选集中进一步过滤。但是我们的 DFS 方法已经足够(每次 O(1e6) 叶子,总共调用约 20 次),总时间 < 1 秒。我们坚持使用简单的循环调用 count_fail(k)。

但是要注意,k=1 到 13 时,组合数很小,计算极快。k=14 时 9e5 叶子。k=15 时 6.3e6。k=16..25 由于剪枝,实际上在 k=15 的基础上,剪枝分支中的候选 r 数量是 A_{15}(大约 2.5e5)。对于 k>15,在剪枝分支中,我们需要检查 r 是否满足第16到第k个素数条件。这部分检查对每个 r 是 O(k-15)。所以 k=16 时,我们需要在 6.3e6 个第15层节点基础上,对每个候选 r 进行后续条件检查?等等,我们的 DFS 结构是:对于 k>=15,我们都会枚举前14个素数的所有组合,以及第15个素数的 7 个分支,然后在第15层节点(即处理完第15个素数后)进入递归下一层(idx=15),此时 M > N,我们立即进行剪枝检查(检查第16到第k个素数)。所以对于每个第15层节点(共 907,200 * 7 = 6.35e6 个),我们都会执行一个循环检查后续条件。对于 k=15,后续没有素数,直接加1(如果 r 有效)。对于 k=16,检查1个素数;k=17 检查2个素数等等。因此总操作数大约是 sum_{k=15}^{K} (6.35e6 * (k-15) * f_avg)。k 最大约 25,所以平均 k-15 ≈ 5,总操作约 6.35e6 * 15 ≈ 1e8 次循环。每次循环包括一个取模和线性搜索(fail集大小约 p/7,平均约 5)。所以 1e8 * 5 ≈ 5e8 次操作,可能稍慢(几秒)。但仍可能在可接受范围内。我们可以进一步优化:不每次都重新 DFS 整个树,而是在找到前15个素数的所有候选 r 后(即 A_{15} 的候选),缓存这些 r,然后后续 k 直接从这些 r 出发过滤。这样我们只需遍历一次完整树到第15层,收集所有有效的 r,然后对每个 k 过滤。

优化方案:

先计算前15个素数的失败条件的所有有效 r(即满足前15个条件且 r ≤ N 且 r 在正确的模 M_15 下的余数)。等等:因为 M_15 > N,满足前15个条件的每个 n 由唯一的 r (mod M_15) 标识,且 r 就是 n 本身(因为 n ≤ N < M_15)。所以 A_{15} 就是满足前15个条件的 n 的集合!我们可以直接收集所有这些 n(即 r 值)。我们不需要存储 M,只需存储 n 的列表。

然后 A_{15} = 列表大小。

对于 k = 16, 17, ...,我们依次过滤列表,保留那些满足第k个素数失败条件的 n。A_k = 过滤后列表大小。

当列表为空时停止。

这样我们只需要枚举前15个素数的一次完整 DFS(到第15层),收集所有符合条件的 r(即 n)。这个枚举量是 6.35e6 个叶子,但我们可以中途剪枝:我们可以在前14个素数的组合基础上,对每个组合计算 r_mod_M14,然后对于第15个素数的每个分支,计算 r_new = r + M_14 * delta。如果 r_new > N 则忽略;如果 r_new == 0 则忽略(因为 n=0 不计)。收集所有有效的 r_new。这些就是 A_{15} 的候选 n。数量约 2.5e5。

然后对于 k=16..K,我们只需扫描列表,删除不满足 primes[k-1] 失败条件的元素。由于列表大小逐渐减小,总操作数为 sum_{k} 列表大小 ≈ A_{15} * (1 + 1/7 + 1/49 + ...) ≈ 2.5e5 * 1.16 ≈ 2.9e5。远远小于之前的 1e8!这极大优化了性能。

但必须小心:A_{14} 怎么得到?A_{14} 也需要计算。但我们可以同时计算 A_1 到 A_{14}。对于 k=1..14,我们可以直接用 count_fail(k) 因为这些组合数很小:A_1..A_13 组合数最多几万,A_14 是 907,200。我们可以轻松分别计算。或者我们也可以在生成候选列表的过程中顺便计算 A_1..A_15?实际上我们仅需要 A_k 用于公式。我们可以分别调用 count_fail(k) 对于 k<=14(非常快),然后对于 k>=15,我们从缓存列表过滤得到 A_k。或者我们统一用过滤法:先得到 A_14 的列表?但 A_14 的 M_14 <= N,所以满足 A_14 条件的 n 是多个等差数列,n 的数量是 A_14 ≈ N * density ≈ 1e17 * (1/210)*(1/7)^10 ≈ 2.5e5 * 7? 我们之前估计 A_15 ≈ 2.5e5,A_14 约是 A_15 的 7 倍?等等密度:density_14 = density_15 * (f(47)/47)? 不对,A_14 是前14个素数失败,A_15 是前15个素数失败。第15个素数 47 的失败密度是 f(47)/47 ≈ 7/47 ≈ 1/6.7。所以 A_15 ≈ A_14 * (7/47)?但 A_14 也是组合数 907,200 每个对应多个 n。实际上 A_14 的数量大约为 N * density_14 = 1e17 * (1/210) * (1/7)^{10} ≈ 1e17 / (210 * 7^10)。7^10 = 282,475,249。210 * 2.82e8 ≈ 5.9e10。所以 A_14 ≈ 1e17 / 5.9e10 ≈ 1.7e6。A_15 是 A_14 的子集,密度再乘以 7/47 ≈ 0.149,所以 A_15 ≈ 2.5e5。这与之前估计一致。

所以我们也可以计算 A_14 的候选集?但 A_14 的每个剩余类 r 对应多个 n,我们无法简单地用列表存储所有 n(因为可能有 1.7e6 个类,每个类有多个 n,总 n 数约 1.7e6 * 平均周期数 ≈ A_14 本身)。存储所有 n 的数量可能达到 1.7e6 个?不,A_14 是满足条件的 n 的总个数,即 count。所以 A_14 ≈ 1.7e6 个数。我们可以存储这 1.7e6 个 n?也许可以,但内存可能稍大(1.7e6 个 long long 约 13 MB),这没问题!但生成这些 n 需要枚举所有组合和每个组合下的所有 n(即循环 k 从 0 到 ...)。这样我们会直接得到所有满足前14个条件的 n 的列表。然后对于 k=15,我们过滤这个列表,保留那些满足第15个素数条件的。然后依次过滤。这可能是最简单且高效的方法!

等等:A_k 是计数,不是列表。在公式中我们只需要 A_k 的数值。但我们通过列表过滤可以得到 A_k 的数值,同时避免了每次重新计算组合。我们可以生成满足前14个条件的 n 的列表,然后依次过滤得到 A_{15}, A_{16}... 直到空。对于 k=1..14,我们可以通过较小规模的组合直接计算 A_k,或者也可以类似地逐步过滤列表,但前14个条件因为 M <= N,组合数本身不大,直接计算计数即可。我们保持简单:对于 k=1..14,使用 count_fail(k) 直接算(速度很快)。对于 k>=15,我们生成 A_{14} 的 n 的列表?可是 A_{14} 本身是通过 count_fail(14) 计算的,我们可以在计算 A_{14} 时顺便生成列表?或者在计算 count_fail(14) 的 DFS 中,当到达叶子节点时,我们有 M_14 和 r,然后遍历所有 k 使得 n = k*M_14 + r <= N,将这些 n 加入列表。这样我们就在 O(组合数 + A_{14}) 时间内得到了列表。组合数 9e5,A_{14} 数量级 1.7e6,总插入操作约 1.7e6,非常快。然后我们对列表进行原地过滤删除,得到 A_{15}, A_{16}... 直到列表空。这样我们就完全避免了重复的 DFS 枚举!

让我们细化这个方案:

预计算 primes 到足够大(比如 200)。

对于 k=1..13,我们直接调用 count_fail(k) 得到 A_k(这些的 M 较小,组合数也小,最多到 k=13 时组合数 129,600? 等等前13个组合数:到41: R=129,600。仍然可以接受)。或者我们也可以对 k=14 直接 count_fail 并同时生成列表。我们统一:对于 k=1..14,使用 count_fail 计算 A_k。对于 k=14,我们使用一个特别版本的 count_fail 或者直接在 count_fail 中把 n 加入 vector。由于 k=14 的 M_14 <= N,我们会在叶子节点调用 count_n,这返回一个数量,我们可以修改为遍历具体的 n 并存储。
实际上,我们可以直接用一个函数计算 A_k 并返回,对于 k=14 另外写一个函数生成列表。

得到列表 list_n = 所有满足前14个素数失败条件的 n (1 ≤ n ≤ N)。
A_{14} = list_n.size()。

对于 k = 15, 16, 17, ...:
从列表中移除不满足 primes[k-1] 失败条件的元素。
A_k = 新列表大小。
如果 A_k == 0: 停止。

使用公式计算 U。

这个方案清晰且高效。生成列表的内存:最多时 A_{14} 约 1.7e6 个 long long,完全适合内存。后续过滤原地删除。

但是 A_{14} 的大小真的是 1.7e6 吗?我们先前估计 1.7e6。实际可能稍多或稍少,但肯定在百万量级,内存安全。

现在需要实现生成 A_{14} 列表的 DFS。我们可以在 DFS 中,当 idx == 14(即处理完前14个素数)时,不是调用 count_n,而是遍历所有满足条件的 n 并 push_back。
注意:前14个素数是 primes[0] 到 primes[13]。
我们需要小心:DFS 中 M_14 是乘积。我们需要计算乘积 M_14 和 r。对于每个叶子,如果 r == 0,n = M_14, 2*M_14, ... <= N。如果 r > 0,n = r, r+M_14, ... <= N。
我们可以统一循环:for (ll n = (r == 0 ? M_14 : r); n <= N; n += M_14) list.push_back(n)。
由于 M_14 ≈ 1.3e16, N = 1e17,每个 r 最多对应约 7 个 n。总 push_back 次数 = A_{14} ≈ 1.7e6。非常快。

所以我们只需要修改 dfs,对于 k=14 的特殊处理。或者我们写一个通用的生成函数。

为了简化,我们可以直接先计算 A_1 到 A_14 用 count_fail,然后再生成列表。生成列表的 DFS 与 count_fail 类似,只是叶子处理不同。我们可以复制 DFS 代码,或者让同一个 DFS 通过函数指针/模板/lambda 来处理叶子。最简单是复制并修改。

现在我们来写代码。

首先我们需要确切知道哪些素数属于前14个。 primes 数组:
0:2, 1:3, 2:5, 3:7, 4:11, 5:13, 6:17, 7:19, 8:23, 9:29, 10:31, 11:37, 12:41, 13:43。
索引13是第14个素数(如果从0开始)。所以 PREFIX_COUNT = 14。 idx 从 0 到 13。

我们需要 M_product 前14个素数。我们可以计算乘积,但要注意可能溢出?前14个乘积 M_14 ≈ 1.3e16 < 9e18,安全。我们可以用一个常量,或者动态计算。动态计算可以在 DFS 过程中自然获得。

现在设计代码结构:

cpp
#include <iostream>
#include <vector>
#include <algorithm>
#include <cstdint>
#include <cstdlib>
using namespace std;
using ll = long long;

vector<int> primes; // 填充到比如100

void fill_primes() {
// 筛法生成素数到100或200
const int MAX_P = 200;
vector<bool> is_prime(MAX_P+1, true);
for (int i=2; i<=MAX_P; ++i) {
if (is_prime[i]) {
primes.push_back(i);
for (int j=i*i; j<=MAX_P; j+=i) is_prime[j] = false;
}
}
}

vector<int> get_fail(int p) {
vector<int> res;
if (p <= 7) {
res.push_back(0);
} else {
for (int k=0; 7*k < p; ++k) {
res.push_back(7*k);
}
}
return res;
}

ll mod_inverse(ll a, int p) {
a %= p;
for (ll t=1; t<p; ++t) {
if ((a*t) % p == 1) return t;
}
return 1;
}

// 函数用于计算 count_n
ll count_n(ll r, ll M, ll N) {
if (r == 0) return N / M;
if (r > N) return 0;
return (N - r) / M + 1;
}

// 计算 A_k 的 DFS
void dfs_count(int idx, int k, ll M, ll r, ll N, ll &ans) {
if (idx == k) {
ans += count_n(r, M, N);
return;
}
int p = primes[idx];
if (M > N) {
if (r == 0 || r > N) return;
for (int j = idx; j < k; ++j) {
int q = primes[j];
int rem = r % q;
bool ok = false;
for (int a : get_fail(q)) {
if (rem == a) { ok = true; break; }
}
if (!ok) return;
}
ans += 1;
return;
}
ll inv = mod_inverse(M % p, p);
for (int a : get_fail(p)) {
ll diff = (a - (r % p)) % p;
if (diff < 0) diff += p;
ll delta = (diff * inv) % p;
ll r_new = r + M * delta;
ll M_new = M * p;
r_new %= M_new;
dfs_count(idx+1, k, M_new, r_new, N, ans);
}
}

ll compute_Ak(int k, ll N) {
ll ans = 0;
dfs_count(0, k, 1, 0, N, ans);
return ans;
}

// 生成列表的 DFS,k = 14 (索引0到13)
void dfs_gen(int idx, ll M, ll r, ll N, vector<ll> &out) {
if (idx == 14) { // 前14个素数处理完
ll start = (r == 0) ? M : r;
for (ll n = start; n <= N; n += M) {
out.push_back(n);
}
return;
}
int p = primes[idx];
if (M > N) {
// 对于生成前14个,M不会大于N,因为M_14<=N。但安全起见保留
if (r == 0 || r > N) return;
// 检查余下条件 (idx..13)
for (int j = idx; j < 14; ++j) {
int q = primes[j];
int rem = r % q;
bool ok = false;
for (int a : get_fail(q)) {
if (rem == a) { ok = true; break; }
}
if (!ok) return;
}
out.push_back(r);
return;
}
ll inv = mod_inverse(M % p, p);
for (int a : get_fail(p)) {
ll diff = (a - (r % p)) % p;
if (diff < 0) diff += p;
ll delta = (diff * inv) % p;
ll r_new = r + M * delta;
ll M_new = M * p;
r_new %= M_new;
dfs_gen(idx+1, M_new, r_new, N, out);
}
}
然后主函数:

cpp
int main() {
fill_primes(); // 确保 primes 至少有 50 个
ll N = 100000000000000000LL; // 10^17

// 计算 A_0 到 A_14
ll U = 0;
ll prev_A = N;
vector<ll> A_vals;
for (int k = 1; k <= 14; ++k) {
ll A_k = compute_Ak(k, N);
ll C = prev_A - A_k;
U += primes[k-1] * C;
cout << "k=" << k << " p=" << primes[k-1] << " A=" << A_k << " C=" << C << " U=" << U << endl;
prev_A = A_k;
if (k == 14) A_vals.push_back(A_k); // A_14
}

// 生成前14个素数的候选集
vector<ll> candidates;
dfs_gen(0, 1, 0, N, candidates);
cout << "A_14 candidates size: " << candidates.size() << endl;
// 应该等于 A_vals[13]? A_14 是之前计算的 prev_A
if (candidates.size() != prev_A) {
cerr << "Mismatch A_14!" << endl;
}

ll A_k = prev_A;
// 继续 k = 15, 16, ...
for (int k = 15; k < (int)primes.size(); ++k) {
int p = primes[k-1]; // 注意 primes 索引:第k个素数在 primes[k-1]
vector<int> fail = get_fail(p);
// 过滤 candidates,保留满足 p 失败条件的
vector<ll> new_candidates;
new_candidates.reserve(candidates.size()); // 预分配
for (ll n : candidates) {
int rem = n % p;
bool ok = false;
for (int a : fail) {
if (rem == a) { ok = true; break; }
}
if (ok) new_candidates.push_back(n);
}
ll A_next = new_candidates.size();
ll C = A_k - A_next;
U += p * C;
cout << "k=" << k << " p=" << p << " A=" << A_next << " C=" << C << " U=" << U << endl;
A_k = A_next;
candidates.swap(new_candidates);
if (A_k == 0) break;
}

cout << "Final U = " << U << endl;
return 0;
}
等一下,A_14 是满足前14个素数失败条件的计数,我们之前用 compute_Ak(14) 计算,并用 dfs_gen 生成列表。但我们需确保 dfs_gen 产生的列表大小等于 A_14。这是正确的,因为逻辑相同。

但我们是否遗漏了:在循环中 k=14 时,我们用 compute_Ak 计算了 A_14。然后 k=15 开始时,我们使用了 prev_A = A_14,以及 candidates 列表。过滤后得到 A_15。然后 U 累加 p_15 * (A_14 - A_15)。这是正确的 C(15) 贡献。后续 k=16 等亦然。

我们需要 primes 数组有足够的素数。我们填充到 200 就足够了(因为 k 大概到 25 左右)。

现在我们需要验证小 N,确保算法正确。由于我无法运行代码,我将推理并尽可能保证正确。我们可以先用已知 U(1470)=4293 进行头脑测试或用小 N 验证。但我信任我们的逻辑。

需要注意的一点:在 dfs_gen 中,我们对于 r == 0 的处理:start = M。但是我们在 count_n 中对于 r == 0 返回 N / M,这意味着 n = M, 2M, ... ≤ N。所以 start = M 正确。

另外,在 dfs_gen 中,我们直接 push_back(n)。对于非常大的 N=10^17,这会产生约 1.7e6 个 long long,内存 ~13 MB,没问题。

然后过滤循环中,我们创建 new_candidates 并 swap。这会导致在过滤时多一份内存,总内存 < 30 MB,安全。

现在,确认 get_fail 函数正确。对于 p <= 7,返回 {0}。对于 p=11,floor(10/7)=1,k=0,1 => {0,7}。对于 p=13 => {0,7}。p=17 => 16/7=2 => {0,7,14}。等等。

我们还要确认在公式中是否包含了所有 n。 U = Σ p * C(p)。C(p) = A_{k-1} - A_k。我们从 k=1 开始,p_1=2, A_0=N。k=14 对应 p_14=43。我们在这个循环中已经累加了 k=1..14 的贡献。然后从 k=15 开始继续累加。最终 A_K 变为 0,这意味着所有 n 都分配完毕。总 C 之和应等于 N。我们可以加一个验证。

现在还需注意一点:我们的候选列表是在“前14个素数失败”条件下生成的,然后我们过滤“第15个素数失败”得到 A_15,“第16个素数失败”得到 A_16,等等。这要求我们的失败条件顺序与素数升序完全一致。这是对的。u(n) 是按素数升序检查的。

但有一个微妙的点:当我们说“前14个素数失败”时,我们实际上要求 n 同时满足对于 p=2,3,5,7,11,13,17,19,23,29,31,37,41,43 的失败条件。然后我们继续检查 p=47。这正确。

现在,我们检查 N=1470 的情况,看看我们的算法框架是否会有问题。对于 N=1470,M_4 = 210, M_5 = 2310 > N。所以我们的“前缀” M 可能很早就会超过 N。我们之前的固定 PREFIX_COUNT = 14 是针对 N=10^17 的。对于不同的 N,前缀 M 超过 N 的时机不同。我们的代码目前是硬编码 PREFIX_COUNT=14,这仅适用于 N=10^17 左右。如果我们要使代码对任意 N 通用,可以动态选择 PREFIX_COUNT。但题目只要求 N=10^17,所以硬编码是可以的。但为了代码的健壮性,我们可以动态决定前缀大小,比如找到最大的 k 使得 M_k <= N,且 R_k 不是太大。或者我们可以直接使用通用的 DFS 剪枝方法,不硬编码前缀大小。我们最初的方案(每次调用 compute_Ak)是通用的,且根据之前分析对于 N=10^17 也足够快(可能几秒)。哪个更好?通用且没有硬编码的方案更好,因为它不依赖特定 N,并且更不容易出错。我们之前的复杂度分析表明即使重复调用 compute_Ak,总操作数也在可接受范围(1e8 次操作)。为了代码更简洁且不需要生成候选列表再过滤的复杂逻辑,我选择回归到通用的 compute_Ak 循环方法,但加强剪枝,并且不缓存前缀。这样代码更短,且不易出错。

但是之前我们发现了 Fail 集变大的问题,这导致组合数变大。对于通用 compute_Ak,组合数最多到 6.35e6 叶子节点(对于 k=15),后续 k 在该层剪枝,每次调用都要遍历这 6.35e6 叶子。我们评估总操作约 1e8,实际上对现代 CPU 这只需约 0.5 秒(如果优化良好)。为了避免潜在的超时,我们可以保留缓存前缀的方法,因为它更高效。既然已经设计了缓存方法,我们就使用缓存方法,但为了健壮性,我们可以动态决定前缀大小:找到最大的 k,使得前 k 个素数的乘积 M_k <= N 且组合数 R_k 不太大(比如 <= 2e6)。对于 N=10^17,我们计算得出 k=14 满足 M_14 ≈ 1.3e16 <= N,R_14 = 907,200。k=15 的 M_15 ≈ 6.1e17 > N,但 R_15 = 6.3e6,如果选 k=15 作为前缀,生成列表时会有 6.3e6 个叶子,产生的候选数量 A_15 ≈ 2.5e5。也是可以的。选择哪个?我们选择 k=14 更好,因为叶子数更少(9e5 vs 6.3e6)。我们可以在代码中动态计算前缀长度:依次计算乘积和组合数,找到乘积 <= N 且组合数最大的 k。或者简单硬编码,因为题目 N 固定。硬编码 14 可以,但我们需要在代码中注明这是针对 10^17 的优化,或者直接计算前缀。

为求稳,我可以实现自动寻找合适前缀的逻辑:

cpp
int prefix_k = 0;
ll M = 1;
ll combos = 1;
for (int i = 0; i < primes.size(); ++i) {
int p = primes[i];
ll next_M = M * p;
int f = get_fail(p).size();
if (next_M > N || combos * f > 2000000) { // 限制组合数
break;
}
M = next_M;
combos *= f;
prefix_k = i + 1;
}
这样对于 10^17,prefix_k 会是 14(因为 M_14 <= 1e17,M_15 > 1e17,combos 在 14 时 907200 <= 2e6,15 时 6.3e6 > 2e6)。这样很灵活。我们就采用动态前缀。

但等等:如果使用动态前缀,后续 k 从 prefix_k+1 开始过滤,那么我们需要生成前 prefix_k 个素数失败条件的候选列表。我们之前的 dfs_gen 是针对固定 14 的,需要改为接受 prefix_k。我们可以轻松修改:dfs_gen 增加参数 prefix_k。

让我们重新组织代码,使其清晰健壮:

cpp
#include <iostream>
#include <vector>
#include <algorithm>
#include <cstdint>
using namespace std;
using ll = long long;

vector<int> primes;

void fill_primes(int max_p) {
vector<bool> is_prime(max_p+1, true);
for (int i=2; i<=max_p; ++i) {
if (is_prime[i]) {
primes.push_back(i);
for (int j=i*i; j<=max_p; j+=i) is_prime[j] = false;
}
}
}

vector<int> get_fail(int p) {
vector<int> res;
if (p <= 7) {
res.push_back(0);
} else {
for (int k=0; 7*k < p; ++k) {
res.push_back(7*k);
}
}
return res;
}

ll mod_inverse(ll a, int p) {
a %= p;
for (ll t=1; t<p; ++t) {
if ((a*t) % p == 1) return t;
}
return 1;
}

ll count_n(ll r, ll M, ll N) {
if (r == 0) return N / M;
if (r > N) return 0;
return (N - r) / M + 1;
}

// 通用 DFS 计算 A_k (用于 k <= prefix_k)
void dfs_count(int idx, int k, ll M, ll r, ll N, ll &ans) {
if (idx == k) {
ans += count_n(r, M, N);
return;
}
int p = primes[idx];
if (M > N) {
if (r == 0 || r > N) return;
for (int j = idx; j < k; ++j) {
int q = primes[j];
int rem = r % q;
bool ok = false;
for (int a : get_fail(q)) {
if (rem == a) { ok = true; break; }
}
if (!ok) return;
}
ans += 1;
return;
}
ll inv = mod_inverse(M % p, p);
for (int a : get_fail(p)) {
ll diff = (a - (r % p)) % p;
if (diff < 0) diff += p;
ll delta = (diff * inv) % p;
ll r_new = r + M * delta;
ll M_new = M * p;
r_new %= M_new;
dfs_count(idx+1, k, M_new, r_new, N, ans);
}
}

ll compute_Ak(int k, ll N) {
ll ans = 0;
dfs_count(0, k, 1, 0, N, ans);
return ans;
}

// 生成满足前 prefix_k 个素数失败条件的 n 的列表
void generate_candidates(int prefix_k, ll N, vector<ll> &out) {
// 使用迭代或递归,这里用递归 lambda 或函数
// 由于需要捕获 out 和 prefix_k,我们使用 function 或直接写递归函数
function<void(int, ll, ll)> dfs_gen = [&](int idx, ll M, ll r) {
if (idx == prefix_k) {
ll start = (r == 0) ? M : r;
for (ll n = start; n <= N; n += M) {
out.push_back(n);
}
return;
}
int p = primes[idx];
if (M > N) {
if (r == 0 || r > N) return;
for (int j = idx; j < prefix_k; ++j) {
int q = primes[j];
int rem = r % q;
bool ok = false;
for (int a : get_fail(q)) {
if (rem == a) { ok = true; break; }
}
if (!ok) return;
}
out.push_back(r);
return;
}
ll inv = mod_inverse(M % p, p);
for (int a : get_fail(p)) {
ll diff = (a - (r % p)) % p;
if (diff < 0) diff += p;
ll delta = (diff * inv) % p;
ll r_new = r + M * delta;
ll M_new = M * p;
r_new %= M_new;
dfs_gen(idx+1, M_new, r_new);
}
};
dfs_gen(0, 1, 0);
}

int main() {
fill_primes(300); // 足够多的素数
ll N = 100000000000000000LL; // 10^17

// 动态决定前缀长度
int prefix_k = 0;
ll M = 1;
ll combos = 1;
for (int i = 0; i < (int)primes.size(); ++i) {
int p = primes[i];
vector<int> fail = get_fail(p);
ll next_M = M * p;
ll next_combos = combos * fail.size();
if (next_M > N || next_combos > 2000000) break;
M = next_M;
combos = next_combos;
prefix_k = i + 1;
}
cout << "Prefix k = " << prefix_k << " M = " << M << " combos = " << combos << endl;

// 计算 A_0 到 A_{prefix_k}
ll U = 0;
ll prev_A = N;
for (int k = 1; k <= prefix_k; ++k) {
ll A_k = compute_Ak(k, N);
ll C = prev_A - A_k;
U += primes[k-1] * C;
// cout << "k=" << k << " p=" << primes[k-1] << " A=" << A_k << " C=" << C << " U=" << U << endl;
prev_A = A_k;
}

// 生成候选列表
vector<ll> candidates;
generate_candidates(prefix_k, N, candidates);
cout << "Candidates size: " << candidates.size() << endl;
if ((ll)candidates.size() != prev_A) {
cerr << "Error: candidates size mismatch!" << endl;
return 1;
}

ll A_k = prev_A;
// 继续过滤
for (int k = prefix_k + 1; k <= (int)primes.size(); ++k) {
int p = primes[k-1];
vector<int> fail = get_fail(p);
vector<ll> new_candidates;
new_candidates.reserve(candidates.size());
for (ll n : candidates) {
int rem = n % p;
bool ok = false;
for (int a : fail) {
if (rem == a) { ok = true; break; }
}
if (ok) new_candidates.push_back(n);
}
ll A_next = new_candidates.size();
ll C = A_k - A_next;
U += p * C;
cout << "k=" << k << " p=" << p << " A=" << A_next << " C=" << C << " U=" << U << endl;
A_k = A_next;
candidates.swap(new_candidates);
if (A_k == 0) break;
}

cout << "Final U = " << U << endl;
return 0;
}
现在我们需要确保 primes 数组至少包含所需的所有素数。如果 candidates 非空且 primes 用完,循环会结束但可能没有完全分配所有 n。但我们的 fill_primes(300) 提供了充足素数。

另外,注意在 generate_candidates 中我们使用了 function 递归,可能会有一些开销,但组合数不到百万,没问题。

我们还需确认在 compute_Ak 中,当 k <= prefix_k 时,M <= N 恒成立(根据我们选择前缀的逻辑,M_prefix <= N)。因此 M > N 的剪枝分支在这些调用中实际不会进入,但保留无妨。

我们还要注意 count_n 和 generate_candidates 中对于 r=0 时 start = M。这意味着 n 从 M 开始,不包括 0。正确。

验证:对于 N=1470,动态前缀会是多少?
M_4 = 210, combos = 1; M_5 = 2310 > 1470; 所以 prefix_k = 4。组合数 combos = 1 (因为前4个素数 f=1)。然后 compute_Ak 会计算 k=1..4。generate_candidates 生成满足 2,3,5,7 失败的数,即 210 的倍数 ≤ 1470:210,420,630,840,1050,1260,1470。共7个。A_4 = 7。然后继续过滤 k=5 (11) 等。这符合之前计算。所以算法对小 N 也正确。

现在我们确认 U(1470) 用此逻辑应该得到 4293。由于我无法运行,但我们可以信任。

最后,U(10^17) 的计算结果需要输出。由于我无法执行代码,需要提供代码作为答案。通常此类题目要求给出计算结果的程序,用户可能希望得到代码以自己运行得到答案。因此提供完整可运行代码即可。

为了完整性,我会在代码中加上注释,解释逻辑。并提供最终代码。
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
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
#include <iostream>
#include <vector>
#include <algorithm>
#include <functional>
#include <cstdint>

using namespace std;
using ll = long long;

// 前 100 个素数 (实际只会用到前几十个)
vector<int> primes;

void fill_primes(int max_p) {
vector<bool> is_prime(max_p + 1, true);
for (int i = 2; i <= max_p; ++i) {
if (is_prime[i]) {
primes.push_back(i);
for (long long j = 1LL * i * i; j <= max_p; j += i)
is_prime[j] = false;
}
}
}

// 对于素数 p,返回"余数是 7 的倍数"的所有可能余数
vector<int> get_fail(int p) {
vector<int> res;
if (p <= 7) {
res.push_back(0); // 只有 0 是 7 的倍数
} else {
for (int k = 0; 7 * k < p; ++k)
res.push_back(7 * k); // 0, 7, 14, ... < p
}
return res;
}

// 求 a 在模 p 下的逆元 (p 为素数, a 与 p 互素)
ll mod_inverse(ll a, int p) {
a %= p;
for (ll t = 1; t < p; ++t)
if ((a * t) % p == 1) return t;
return 1; // 不会执行到
}

// 统计满足 n ≡ r (mod M) 且在 [1, N] 内的 n 的个数
ll count_n(ll r, ll M, ll N) {
if (r == 0) return N / M; // n = M, 2M, ...
if (r > N) return 0;
return (N - r) / M + 1;
}

// 递归计算满足前 k 个素数失败条件的 n 的数量
void dfs_count(int idx, int k, ll M, ll r, ll N, ll &ans) {
if (idx == k) {
ans += count_n(r, M, N);
return;
}
int p = primes[idx];
if (M > N) { // 当前模已经大于 N,至多有一个候选 r
if (r == 0 || r > N) return;
for (int j = idx; j < k; ++j) {
int q = primes[j];
int rem = r % q;
bool ok = false;
for (int a : get_fail(q))
if (rem == a) { ok = true; break; }
if (!ok) return;
}
ans += 1;
return;
}
ll inv = mod_inverse(M % p, p);
for (int a : get_fail(p)) {
ll diff = (a - (r % p)) % p;
if (diff < 0) diff += p;
ll delta = (diff * inv) % p;
ll r_new = r + M * delta;
ll M_new = M * p;
r_new %= M_new;
dfs_count(idx + 1, k, M_new, r_new, N, ans);
}
}

ll compute_Ak(int k, ll N) {
ll ans = 0;
dfs_count(0, k, 1, 0, N, ans);
return ans;
}

// 生成所有满足前 prefix_k 个素数失败条件的 n (1 ≤ n ≤ N)
void generate_candidates(int prefix_k, ll N, vector<ll> &out) {
function<void(int, ll, ll)> dfs_gen = [&](int idx, ll M, ll r) {
if (idx == prefix_k) {
ll start = (r == 0) ? M : r;
for (ll n = start; n <= N; n += M)
out.push_back(n);
return;
}
int p = primes[idx];
if (M > N) {
if (r == 0 || r > N) return;
for (int j = idx; j < prefix_k; ++j) {
int q = primes[j];
int rem = r % q;
bool ok = false;
for (int a : get_fail(q))
if (rem == a) { ok = true; break; }
if (!ok) return;
}
out.push_back(r);
return;
}
ll inv = mod_inverse(M % p, p);
for (int a : get_fail(p)) {
ll diff = (a - (r % p)) % p;
if (diff < 0) diff += p;
ll delta = (diff * inv) % p;
ll r_new = r + M * delta;
ll M_new = M * p;
r_new %= M_new;
dfs_gen(idx + 1, M_new, r_new);
}
};
dfs_gen(0, 1, 0);
}

int main() {
fill_primes(300);
ll N = 100000000000000000LL; // 10^17

// 动态选择一个合适的前缀长度:
// 其模乘积 M ≤ N 且组合数不太大 (≤ 2e6)
int prefix_k = 0;
ll M = 1, combos = 1;
for (int i = 0; i < (int)primes.size(); ++i) {
int p = primes[i];
vector<int> fail = get_fail(p);
ll next_M = M * p;
ll next_combos = combos * fail.size();
if (next_M > N || next_combos > 2000000) break;
M = next_M;
combos = next_combos;
prefix_k = i + 1;
}
cout << "Prefix: " << prefix_k << " primes, M = " << M
<< ", combinations = " << combos << endl;

// 1. 计算 k = 1 .. prefix_k 的 A_k 以及相应的 U 部分
ll U = 0;
ll prev_A = N;
for (int k = 1; k <= prefix_k; ++k) {
ll A_k = compute_Ak(k, N);
ll C = prev_A - A_k;
U += primes[k - 1] * C;
// cout << "k=" << k << " p=" << primes[k-1] << " A=" << A_k
// << " C=" << C << " U=" << U << endl;
prev_A = A_k;
}

// 2. 生成满足前 prefix_k 个条件的全部 n
vector<ll> candidates;
generate_candidates(prefix_k, N, candidates);
cout << "Candidates after prefix: " << candidates.size() << endl;
if ((ll)candidates.size() != prev_A) {
cerr << "Mismatch!" << endl;
return 1;
}

// 3. 逐个增加素数继续过滤,直到没有候选者
ll A_k = prev_A;
for (int k = prefix_k + 1; k <= (int)primes.size(); ++k) {
int p = primes[k - 1];
vector<int> fail = get_fail(p);
vector<ll> new_candidates;
new_candidates.reserve(candidates.size());
for (ll n : candidates) {
int rem = n % p;
bool ok = false;
for (int a : fail)
if (rem == a) { ok = true; break; }
if (ok) new_candidates.push_back(n);
}
ll A_next = new_candidates.size();
ll C = A_k - A_next;
U += p * C;
cout << "k=" << k << " p=" << p << " A=" << A_next
<< " C=" << C << " U=" << U << endl;
A_k = A_next;
candidates.swap(new_candidates);
if (A_k == 0) break;
}

cout << "Final U(10^17) = " << U << endl;
return 0;
}

fun fact

ds一发过了倒是.
其实是我的话估计得找规律找半天.