杜教筛 学习笔记
杜教筛用于计算某一类积性函数的前缀和。
思想
构造 Dirchlet 卷积,把一个困难的前缀和转化为两个相对简单的前缀和。
形式化地说,我们有数论函数
也就是说,我们有:
这里如果我们假设
好,我们解决了单次计算的循环次数问题,那么我们发现每次递归的
但是我们发现,对于较小的
记忆化的部分,我们可以直接记录一个 ans[x]
代表
例题
P4213 【模板】杜教筛(Sum)
给定一个正整数,求
ans_1=\sum_{i=1}^n\varphi(i)
ans_2=\sum_{i=1}^n \mu(i)
1 \leq n \lt 2^{31}
对于
对于
参考代码:
const int N = 5e6 + 10;
const int M = 5e3 + 10;
ll phi[N],maxn,maxm,n,mu[N],pri[N],tot,prephi[M],premu[M];
bool vis[N];
void init() {
phi[1] = mu[1] = 1;
for (int i = 2;i <= N-10;++i) {
if (!vis[i]) pri[++tot] = i,mu[i] = -1,phi[i] = i - 1;
for (int j = 1;j <= tot && pri[j] * i <= N - 10;++j) {
vis[pri[j] * i] = 1;
if (i % pri[j] == 0) {
phi[i * pri[j]] = phi[i] * pri[j];
break;
}
phi[i * pri[j]] = phi[i] * phi[pri[j]];
mu[i * pri[j]] = -mu[i];
}
}
for (int i = 1;i <= N-10;++i) mu[i] += mu[i-1],phi[i] += phi[i-1];
}//在O(n^c)的时间内进行预处理。
ll getphi(ll x) {
if (x < N-10) return phi[x];
ll now = n / x;
if (prephi[now]) return prephi[now];
//注意这边,我们记忆化的实际上是n / x的值,因为我们递归调用到的总是一个形如n/x的形式,所以这里的数组可以开的相对小一点
ll ans = x * (x + 1) / 2;//\sum f*g
for (ll l = 2,r;l <= x;l = r + 1) {
r = x / (x / l);
ans -= (r - l + 1) * getphi(x / l);//-\sum g
}
return prephi[now] = ans;//记得记忆化
}
ll getmu(ll x) {
if (x < N-10) return mu[x];
ll now = n / x;
if (premu[now]) return premu[now];
ll ans = 1;
for (ll l = 2,r;l <= x;l = r + 1) {
r = x / (x / l);
ans -= (r - l + 1) * getmu(x / l);
}
return premu[now] = ans;
}
P3768 简单的数学题
求
\left(\sum_{i=1}^n\sum_{j=1}^n ij \gcd(i,j)\right) \bmod p
n \leq 10^{10}
那么我们要求的就是
那么我们总的式子就变成了:
别急,按杜教筛套路继续推下去,我们令
我们来考虑这个
这个是典型的杜教筛式,所以我们就数论分块里面再套一个杜教筛就OK了。
代码:
const int N = 8000000 + 10;
const int M = 1e5 + 10;
ll phi[N],maxn,maxm,n,mu[N],pri[N],tot,premu[M];
ll mod;
bool vis[N];
unordered_map <ll,ll> prephi;
void init() {
phi[1] = mu[1] = 1;
for (int i = 2;i <= N-10;++i) {
if (!vis[i]) pri[++tot] = i,mu[i] = -1,phi[i] = i - 1;
for (int j = 1;j <= tot && pri[j] * i <= N - 10;++j) {
vis[pri[j] * i] = 1;
if (i % pri[j] == 0) {
phi[i * pri[j]] = phi[i] * pri[j];
break;
}
phi[i * pri[j]] = phi[i] * phi[pri[j]];
mu[i * pri[j]] = -mu[i];
}
}
for (ll i = 1;i <= N-10;++i) phi[i] = (phi[i-1] + phi[i] * i % mod * i % mod) % mod;
}
ll inv2,inv6;
ll sum2(ll x) {
x %= mod;
return x * (x + 1) % mod * inv2 % mod;
}
ll sum3(ll x) {
x %= mod;
return x * (x + 1) % mod * (2 * x % mod + 1) %mod *inv6 %mod;
}
ll dusieve(ll x) {
//printf("%lld ",x);
if (x < N-10) return phi[x];
ll now = n / x;
if (prephi[x]) return prephi[x];
ll ans = sum2(x) * sum2(x) % mod;
for (ll l = 2,r;l <= x;l = r + 1) {
r = x / (x / l);
ans = ((ans - (((sum3(r) - sum3(l-1)) % mod + mod) % mod * dusieve(x / l))) % mod + mod) % mod;
}
return prephi[x] = (ans + mod) % mod;
}
ll ffpow(ll a,ll b) {
a %= mod;
ll ans = 1;
for (;b;b >>= 1) {
if (b & 1) ans = ans * a % mod;
a = a * a % mod;
}
return ans;
}
signed main() {
read(mod,n);
inv2 = ffpow(2,mod - 2);inv6 = ffpow(6,mod - 2);
init();
ll ans = 0;
for (ll l = 1,r;l <= n;l = r + 1) {
r = n / (n / l);
ll a1 = sum2(n / l) * sum2(n / l) % mod;
ll a2 = ((dusieve(r) - dusieve(l - 1)) % mod + mod) % mod;
ans = (ans + (a1 * a2) % mod) % mod;
}
if (ans < 0) ans += mod;
printf("%lld\n",ans % mod);
return 0;
}
本作品采用 知识共享署名-相同方式共享 4.0 国际许可协议 进行许可。
《孽欲狂情》科幻片高清在线免费观看:https://www.jgz518.com/xingkong/125001.html
卷王
/xia