min_25 筛
一个亚线性筛,复杂度大概是。
使用求前缀和,有两个基本特征:① 积性函数,② 满足质数点为多项式。
算法思路
给定,设为一个积性函数,质数点为多项式,求。
先求出内的所有质数,以及。
求出,为第个质因子。
我们设,质数或者最小质因子大于的值之和。
考虑从推到,我们也就是要减去最小质因子等于的贡献,
考虑如何递归计算积性函数前缀和。
设,最小质因子大于等于的值之和,所以这个积性函数的前缀和即为。
对于,中质数的贡献我们是已知的,接下来考虑计算合数对答案的贡献。
我们枚举合数的最小质因子以及最小质因子的次幂,考虑从推到,也就是在原来的基础上加上,
所以有
这里的是最后筛得的,也就是里面存的是质数的前缀和。
这里解释一下那后面一长串的东西,为什么要加上,而且上界为,
由于我们枚举的是最小质因子及幂次,那么一定有,所以有上界为,
至于为什么要加上,由于我们定义的函数中在执行的过程中始终为,
当我们枚举的幂次为时,我们会漏了这一项,所以得在前一步给他加上去。
优化一点点?如何递推计算积性函数前缀和。
不就是把上面的,从一步一步往下推吗?
对于,这一步,中我们递归会返回的是,
也就是大于的质数的部分,按照的定义,我们在递推的时候,得到的却是。
所以我们修改一下递推时候的的定义:设,
我们还是同样的枚举最小质因子,及其次幂,考虑从推到,也就是要加上
P5325 【模板】Min_25筛
有积性函数,且,求。
#include <bits/stdc++.h> using namespace std; namespace min_25 { const int N = 1e6 + 10, mod = 1e9 + 7, inv2 = 500000004, inv6 = 166666668; int prime[N], id1[N], id2[N], m, T, cnt; long long a[N], sum1[N], g1[N], sum2[N], g2[N], s[N], n; bool st[N]; inline int ID(long long x) { return x <= T ? id1[x] : id2[n / x]; } inline long long f(long long x) { x %= mod; return x * (x - 1) % mod; } inline long long calc1(long long x) { x %= mod; return (x * (x + 1) % mod * (2 * x + 1) % mod * inv6 % mod - 1 + mod) % mod; } inline long long calc2(long long x) { x %= mod; return (x * (x + 1) % mod * inv2 % mod - 1 + mod) % mod; } void init() { T = sqrt(n + 0.5); for(int i = 2; i <= T; i++) { if(!st[i]) { prime[++cnt] = i; sum1[cnt] = (sum1[cnt - 1] + 1ll * i * i) % mod; sum2[cnt] = (sum2[cnt - 1] + i) % mod; } for(int j = 1; j <= cnt && 1ll * i * prime[j] <= T; j++) { st[i * prime[j]] = 1; if(i % prime[j] == 0) { break; } } } for (long long l = 1, r; l <= n; l = r + 1) { r = n / (n / l); a[++m] = n / l; a[m] <= T ? id1[a[m]] = m : id2[n / a[m]] = m; g1[m] = calc1(a[m]); g2[m] = calc2(a[m]); } for (int j = 1; j <= cnt && 1ll * prime[j] * prime[j] <= n; j++) { for (int i = 1; i <= m && 1ll * prime[j] * prime[j] <= a[i]; i++) { g1[i] = ((g1[i] - 1ll * prime[j] * prime[j] % mod * (g1[ID(a[i] / prime[j])] - sum1[j - 1]) % mod) % mod + mod) % mod; g2[i] = ((g2[i] - 1ll * prime[j] * (g2[ID(a[i] / prime[j])] - sum2[j - 1]) % mod) % mod + mod) % mod; } } for (int i = 1; i <= m; i++) { s[i] = (g1[i] - g2[i] + mod) % mod; } for (int j = cnt; j >= 1; j--) { for (int i = 1; i <= m && 1ll * prime[j] * prime[j] <= a[i]; i++) { for (long long cur = prime[j]; cur * prime[j] <= a[i]; cur *= prime[j]) { s[i] = (s[i] + f(cur % mod) * (s[ID(a[i] / cur)] - sum1[j] + sum2[j]) % mod + f(cur * prime[j])) % mod; s[i] = (s[i] + mod) % mod; } } } printf("%lld\n", s[ID(n)] + 1); } } int main() { // freopen("in.txt", "r", stdin); // freopen("out.txt", "w", stdout); scanf("%lld", &min_25::n); min_25::init(); return 0; }
#6235. 区间素数个数
直接是的函数求解。
#include <bits/stdc++.h> using namespace std; typedef long long ll; const int N = 1e6 + 10; int prime[N], id1[N], id2[N], m, cnt, T; bool st[N]; ll a[N], sum[N], g[N], n; inline int ID(ll x) { return x <= T ? id1[x] : id2[n / x]; } void init() { T = sqrt(n + 0.5); for (int i = 2; i <= T; i++) { if (!st[i]) { prime[++cnt] = i; sum[cnt] = sum[cnt - 1] + 1; } for (int j = 1; j <= cnt && 1ll * i * prime[j] <= T; j++) { st[i * prime[j]] = 1; if (i % prime[j] == 0) { break; } } } for (ll l = 1, r; l <= n; l = r + 1) { r = n / (n / l); a[++m] = n / l; if (a[m] <= T) { id1[a[m]] = m; } else { id2[n / a[m]] = m; } g[m] = a[m] - 1; } for (int j = 1; j <= cnt && 1ll * prime[j] * prime[j] <= n; j++) { for (int i = 1; i <= m && 1ll * prime[j] * prime[j] <= a[i]; i++) { g[i] -= g[ID(a[i] / prime[j])] - sum[j - 1]; } } } ll solve(ll n) { return g[ID(n)]; } int main() { // freopen("in.txt", "r", stdin); // freopen("out.txt", "w", stdout); scanf("%lld", &n); init(); printf("%lld\n", solve(n)); return 0; }
#572. 「LibreOJ Round #11」Misaka Network 与求和
为次大质因子,。
设,而有:
$$
对于 ,可以通过杜教筛求得,考虑如何求前面的,对前项考虑求解。
我们定义,也就是质数或者,最小质因子大于等于的答案。
每一轮我们枚举最小质因子及其幂次,考虑这一部分合数的贡献,分两种:
- 是次小质因子。
- 不是次小质因子。
#include <bits/stdc++.h> using namespace std; typedef unsigned int uint; uint quick_pow(uint a, int n) { uint ans = 1; while (n) { if (n & 1) { ans *= a; } a *= a; n >>= 1; } return ans; } int n, k; namespace min_25 { const int N = 1e6 + 10; int prime[N], a[N], id1[N], id2[N], m, cnt, T; uint g[N], sum[N], s[N], f[N]; bool st[N]; inline int ID(int x) { return x <= T ? id1[x] : id2[n / x]; } void init() { T = sqrt(n + 0.5); for(int i = 2; i <= T; i++) { if(!st[i]) { prime[++cnt] = i; f[cnt] = quick_pow(i, k); sum[cnt] = sum[cnt - 1] + 1; } for(int j = 1; j <= cnt && 1ll * i * prime[j] <= T; j++) { st[i * prime[j]] = 1; if(i % prime[j] == 0) { break; } } } for(int l = 1, r; l <= n; l = r + 1) { r = n / (n / l); a[++m] = n / l; a[m] <= T ? id1[a[m]] = m : id2[n / a[m]] = m; g[m] = a[m] - 1; } for(int j = 1; j <= cnt; j++) { for(int i = 1; i <= m && 1ll * prime[j] * prime[j] <= a[i]; i++) { g[i] -= g[ID(a[i] / prime[j])] - sum[j - 1]; } } for (int i = 1; i <= m; i++) { s[i] = g[i]; } for (int j = cnt; j >= 1; j--) { for (int i = 1; i <= m && 1ll * prime[j] * prime[j] <= a[i]; i++) { for (int cur = prime[j]; 1ll * cur * prime[j] <= a[i]; cur *= prime[j]) { s[i] += (s[ID(a[i] / cur)] - g[ID(a[i] / cur)]) + (g[ID(a[i] / cur)] - sum[j - 1]) * f[j]; /* 合数答案贡献分为两类: 一、当前枚举的质数不是次小的, 二、当前枚举的质数是次小的,也就是形如prime[j] ^ k * b, b是一个质数且这个质数大于 prime[j], 最后再加上 f(prime[j] ^ {k + 1})。 */ } } } } uint solve(int x) { return x <= 1 ? 0 : s[ID(x)]; } } namespace Djs { const int N = 2e6 + 10; int prime[N], cnt; uint phi[N]; bool st[N]; void init() { phi[1] = 1; for (int i = 2; i < N; i++) { if (!st[i]) { prime[++cnt] = i; phi[i] = i - 1; } for (int j = 1; j <= cnt && 1ll * i * prime[j] < N; j++) { st[i * prime[j]] = 1; if (i % prime[j] == 0) { phi[i * prime[j]] = phi[i] * prime[j]; break; } phi[i * prime[j]] = phi[i] * (prime[j] - 1); } } for (int i = 1; i < N; i++) { phi[i] += phi[i - 1]; } } unordered_map<int, uint> mp; uint Djs_ans(int n) { if (n < N) { return phi[n]; } if (mp.count(n)) { return mp[n]; } uint ans = 1ll * n * (n + 1) / 2; for (uint l = 2, r; l <= n; l = r + 1) { r = n / (n / l); ans -= (r - l + 1) * Djs_ans(n / l); } return mp[n] = ans; } } int main() { // freopen("in.txt", "r", stdin); // freopen("out.txt", "w", stdout); scanf("%d %d", &n, &k); min_25::init(); Djs::init(); uint ans = 0; for (uint l = 1, r; l <= n; l = r + 1) { r = n / (n / l); ans += (min_25::solve(r) - min_25::solve(l - 1)) * (2 * Djs::Djs_ans(n / l) - 1); } cout << ans << "\n"; return 0; }
1847 奇怪的数学题
给定,要我们求,是的次大公约数,,另。
$\sum\limits_{d = 1} ^{n} f(d) ^ kmin_25f(n) = n ^ k$。
还记得,的转移
后面减去的不就是最小质因子等于且为合数的函数值,那么就是除去的,也就是我们需要的了。
#include <bits/stdc++.h> using namespace std; typedef unsigned int uint; const int N = 1e6 + 10; uint quick_pow(uint a, int n) { uint ans = 1; while(n) { if(n & 1) ans = ans * a; a = a * a; n >>= 1; } return ans; } namespace Sum { uint S[60][60], k; uint Sumk(int n) { uint ret = 0; for(int i = 1; i <= k; ++i) { uint s = 1; for(int j = 0; j <= i; ++j) if((n + 1 - j) % (i + 1)) s *= n + 1 - j; else s *= (n + 1 - j) / (i + 1); ret += S[k][i] *s; } return ret; } void init() { S[0][0] = 1; for(int i = 1; i <= k; i++) for(int j = 1; j <= i; j++) S[i][j] = S[i-1][j-1] + j * S[i-1][j]; } } namespace Min_25 { uint prime[N], id1[N], id2[N], m, cnt, k, T; u***1[N], sum1[N], g2[N], sum2[N], f[N], ans[N], n; bool st[N]; int ID(int x) { return x <= T ? id1[x] : id2[n / x]; } void init() { T = sqrt(n + 0.5); for(int i = 2; i <= T; i++) { if(!st[i]) { prime[++cnt] = i; sum1[cnt] = sum1[cnt - 1] + 1; f[cnt] = quick_pow(i, k); sum2[cnt] = sum2[cnt - 1] + f[cnt]; } for(int j = 1; j <= cnt && 1ll * i * prime[j] <= T; j++) { st[i * prime[j]] = 1; if(i % prime[j] == 0) { break; } } } for(int l = 1, r; l <= n; l = r + 1) { r = n / (n / l); a[++m] = n / l; if(a[m] <= T) id1[a[m]] = m; else id2[n / a[m]] = m; g1[m] = a[m] - 1; g2[m] = Sum::Sumk(a[m]) - 1; } for(int j = 1; j <= cnt; j++) { for(int i = 1; i <= m && 1ll * prime[j] * prime[j] <= a[i]; i++) { g1[i] -= (g1[ID(a[i] / prime[j])] - sum1[j - 1]); g2[i] -= f[j] * (g2[ID(a[i] / prime[j])] - sum2[j - 1]); ans[i] += g2[ID(a[i] / prime[j])] - sum2[j - 1]; } } for(int i = 1; i <= m; i++) { ans[i] += g1[i]; } } uint solve(int x) { if(x <= 1) return 0; return ans[ID(x)]; } } namespace Djs { uint prime[N], phi[N], cnt; bool st[N]; void init() { phi[1] = 1; for(int i = 2; i < N; i++) { if(!st[i]) { prime[++cnt] = i; phi[i] = i - 1; } for(int j = 1; j <= cnt && 1ll * i * prime[j] < N; j++) { st[i * prime[j]] = 1; if(i % prime[j] == 0) { phi[i * prime[j]] = phi[i] * prime[j]; break; } phi[i * prime[j]] = phi[i] * (prime[j] - 1); } } for(int i = 1; i < N; i++) { phi[i] += phi[i - 1]; } } unordered_map<int, uint> ans_s; uint S(int n) { if(n < N) return phi[n]; if(ans_s.count(n)) return ans_s[n]; uint ans = 1ll * n * (n + 1) / 2; for(uint l = 2, r; l <= n; l = r + 1) { r = n / (n / l); ans -=(r - l + 1) * S(n / l); } return ans_s[n] = ans; } } int main() { // freopen("in.txt", "r", stdin); // freopen("out.txt", "w", stdout); int n, k; cin >> n >> k; Sum::k = k; Sum::init(); Djs::init(); Min_25::n = n, Min_25::k = k; Min_25::init(); uint ans = 0; for(uint l = 1, r; l <= n; l = r + 1) { r = n / (n / l); ans += (Min_25::solve(r) - Min_25::solve(l - 1)) * (2 * Djs::S(n / l) - 1); } cout << ans << "\n"; return 0; }
这是第二次学min_25了,第一次学的时候只会打打模板,理解的不是很深,希望这次我是真的理解了吧!!!