【思路要点】
- 用类似 M i n 25 Min25 Min25 筛 的过程进行搜索即可。
- 具体来说,先用线性筛得出 N \sqrt{N} N 以内的质数,记 p r i m e i prime_i primei 表示第 i i i 个质数。
- 定义过程 w o r k ( x , y , z ) work(x,y,z) work(x,y,z) 表示处理大于等于 p r i m e y prime_y primey 的质因子乘积不超过 x x x ,小于 p r i m e y prime_y primey 的质因子的指数信息为 z z z 的数对答案的贡献。
- 我们需要处理的即为 w o r k ( N , 1 , ∅ ) work(N,1,\empty) work(N,1,∅) 。
- 对于 w o r k ( x , y , z ) work(x,y,z) work(x,y,z) ,我们将大于等于 p r i m e y prime_y primey 的质因子乘积为质数的数单独处理,显然,这些数是属于一类的,其个数即为 [ p r i m e y , x ] [prime_y,x] [primey,x] 中质数的个数,可以事先筛出。
- 否则,枚举下一个质因数 p r i m e i prime_i primei ,满足 p r i m e i 2 ≤ x , p r i m e i ≥ p r i m e y prime_i^2≤x,prime_i≥prime_y primei2≤x,primei≥primey ,并枚举其指数 e e e ,满足 p r i m e i e + 1 ≤ x prime_i^{e+1}≤x primeie+1≤x ,调用 w o r k ( ⌊ x p r i m e i e ⌋ , i , z + e ) work(\lfloor\frac{x}{prime_i^e}\rfloor,i,z+e) work(⌊primeiex⌋,i,z+e) 即可。
- 时间复杂度 O ( N P o l y ( L o g N ) ∗ α ) O(\frac{N}{Poly(LogN)}*\alpha) O(Poly(LogN)N∗α) ,其中 α \alpha α 为统计答案带来的复杂度因子。
【代码】
#include<bits/stdc++.h> using namespace std; const int MAXN = 3e5 + 5; typedef long long ll; template <typename T> void chkmax(T &x, T y) {x = max(x, y); } template <typename T> void chkmin(T &x, T y) {x = min(x, y); } template <typename T> void read(T &x) { x = 0; int f = 1; char c = getchar(); for (; !isdigit(c); c = getchar()) if (c == '-') f = -f; for (; isdigit(c); c = getchar()) x = x * 10 + c - '0'; x *= f; } template <typename T> void write(T x) { if (x < 0) x = -x, putchar('-'); if (x > 9) write(x / 10); putchar(x % 10 + '0'); } template <typename T> void writeln(T x) { write(x); puts(""); } ll n, val[MAXN], cnt[MAXN]; int limit, m, home1[MAXN], home2[MAXN]; map <vector <int>, ll> ans; int tot, prime[MAXN], f[MAXN]; void getans(ll x, int y, vector <int> now) { now.push_back(1); sort(now.begin(), now.end(), [&] (int x, int y) {return x > y; }); ll delta = -(y - 1); if (x <= limit) delta += cnt[home1[x]]; else delta += cnt[home2[n / x]]; if (delta > 0) ans[now] += delta; } void sieve() { for (ll i = 1, nxt; i <= n; i = nxt + 1) { ll tmp = n / i; val[++m] = tmp; cnt[m] = tmp - 1; if (tmp <= limit) home1[tmp] = m; else home2[n / tmp] = m; nxt = n / tmp; } for (int i = 1; i <= tot; i++) { for (int j = 1; 1ll * prime[i] * prime[i] <= val[j]; j++) { ll tmp = val[j] / prime[i]; int pos = 0; if (tmp <= limit) pos = home1[tmp]; else pos = home2[n / tmp]; cnt[j] -= cnt[pos] - (i - 1); } } } void work(ll x, int y, vector <int> now) { getans(x, y, now); if (x < prime[y] || y > tot) return; for (int i = y; i <= tot && 1ll * prime[i] * prime[i] <= x; i++) { ll val = prime[i], tmp = 1ll * prime[i] * prime[i]; for (int j = 1; tmp <= x; j++, val = tmp, tmp *= prime[i]) { vector <int> tnow = now; tnow.push_back(j + 1); sort(tnow.begin(), tnow.end(), [&] (int x, int y) {return x > y; }); ans[tnow]++; now.push_back(j); work(x / val, i + 1, now); now.pop_back(); } } } void init(int n) { for (int i = 2; i <= n; i++) { if (f[i] == 0) prime[++tot] = f[i] = i; for (int j = 1; j <= tot && prime[j] <= f[i]; j++) { int tmp = prime[j] * i; if (tmp > n) break; f[tmp] = prime[j]; } } } int main() { read(n); limit = sqrt(n) + 1; init(limit + 100); sieve(); vector <int> tmp; tmp.clear(); work(n, 1, tmp); printf(": 1\n"); for (auto x : ans) { static int cnt[36]; ll ans = x.second; memset(cnt, 0, sizeof(cnt)); for (auto y : x.first) { printf("%d ", y); ans *= ++cnt[y]; } printf(": %lld\n", ans); } return 0; }