存在
首先
首先考虑若我们将
于是不难发现对于质数
至此不难想到,我们现在已知
考虑如何分解,显然对于 unordered_map
里,同时需要记录基于的质数,而若其存在
于是可以考虑根号枚举
此时可以通过背包处理,即 unordered_map
维护,而记录基于某个质数便是为了在转移的时候不出现同时使用多个质数的情况,所以对每个质数开个 basic_string
记录其所有合法的 unordered_map
。
尝试分析复杂度,考虑 DP 时,显然枚举素数时最多有
Tips:注意计算过程中包括但不限于 Miller-Rabin 有多处会超过 long long
范围,需要开 __int128_t
或用 long double
快速乘,可以通过 Sanitizer 检测 signed-integer-overflow 并输入最大的数据进行测试寻找报错。
Upd:注意在 Miller-Rabin 中若我们使用固定的基底,那么验证时需要对于基底进行
x
1
2
3
4
5
6
7
8
9
10
11
12using namespace std;
13
14mt19937 rnd(random_device{}());
15int rndd(int l, int r){return rnd() % (r - l + 1) + l;}
16bool rnddd(int x){return rndd(1, 100) <= x;}
17
18typedef unsigned int uint;
19typedef unsigned long long unll;
20typedef long long ll;
21typedef long double ld;
22
23
24
25template < typename T = int >
26inline T read(void);
27
28ll N;
29unordered_map < ll, ll > dp[2];
30basic_string < ll > Prime;
31unordered_map < ll, ll > pows;
32bitset < LIM + 100 > notPrime;
33unordered_map < ll, basic_string < ll > > tpow;
34basic_string < ll > tot;
35
36namespace MillerRabin{
37 int radix[10] = {0, 2, 3, 5, 7, 11, 13, 19, 17};
38 ll qpow(ll a, ll b, ll MOD){
39 ll ret(1), mul(a);
40 while(b){
41 if(b & 1)ret = (__int128_t)ret * mul % MOD;
42 b >>= 1;
43 mul = (__int128_t)mul * mul % MOD;
44 }return ret;
45 }
46 bool Check(ll N){
47 if(N <= 2 || !(N & 1))return N == 2;
48 ll base(N - 1); int cpow(0);
49 while(base % 2 == 0)base >>= 1, ++cpow;
50 for(int t = 1; t <= 8; ++t){
51 if(radix[t] % N == 0)continue;
52 ll cur = qpow(radix[t] % N, base, N);
53 if(cur == 1)continue;
54 for(int i = 1; i <= cpow; ++i){
55 if(cur == N - 1)break;
56 cur = (__int128_t)cur * cur % N;
57 if(i == cpow)return false;
58 }
59 }return true;
60 }
61};
62
63void Sieve(void){
64 for(ll i = 2; i <= LIM; ++i){
65 if(!notPrime[i])Prime += i;
66 for(auto p : Prime){
67 if(p * i > LIM)break;
68 notPrime[p * i] = true;
69 if(i % p == 0)break;
70 }
71 }
72}
73void Init(void){
74 Sieve();
75 for(auto p : Prime){
76 ll base(p);
77 while(base <= N)pows[base] = p, base *= p;
78 }
79 for(ll i = 1; i * i <= N; ++i){
80 if(N % i)continue;
81 if(pows.find(i - 1) != pows.end())
82 tpow[pows[i - 1]] += i, tot += pows[i - 1];
83 if(i * i != N){
84 if(pows.find(N / i - 1) != pows.end())
85 tpow[pows[N / i - 1]] += N / i, tot += pows[(N / i) - 1];
86 else if(MillerRabin::Check(N / i - 1))
87 tpow[N / i - 1] += N / i, tot += N / i - 1;
88 }
89 }
90 sort(tot.begin(), tot.end());
91 tot.erase(unique(tot.begin(), tot.end()), tot.end());
92}
93
94int main(){
95 N = read < ll >();
96 Init();
97 dp[0][1] = 1;
98 int base(0);
99 for(auto p : tot){
100 base ^= 1, dp[base] = dp[base ^ 1];
101 for(auto lst : dp[base ^ 1])
102 for(auto pri : tpow[p])
103 if((__int128_t)pri * lst.first <= N && N % (pri * lst.first) == 0)
104 dp[base][pri * lst.first] += lst.second;
105 }printf("%lld\n", dp[base][N]);
106 fprintf(stderr, "Time: %.6lf\n", (double)clock() / CLOCKS_PER_SEC);
107 return 0;
108}
109
110template < typename T >
111inline T read(void){
112 T ret(0);
113 int flag(1);
114 char c = getchar();
115 while(c != '-' && !isdigit(c))c = getchar();
116 if(c == '-')flag = -1, c = getchar();
117 while(isdigit(c)){
118 ret *= 10;
119 ret += int(c - '0');
120 c = getchar();
121 }
122 ret *= flag;
123 return ret;
124}
update-2023_02_10 初稿
update-2023_02_10 修正一些不符合规范的错误
update-2023_02_10 修正 Miller-Rabin 中的错误