【luogu】简单的数学题(莫比乌斯反演、杜教筛)

it2022-05-05  116

文章目录

题意思路注意代码

题意

给出 n n n p , ( n ≤ 1 0 10 ) p,(n \le 10^{10}) p,(n1010),求

∑ i = 1 n ∑ j = 1 n i ⋅ j ⋅ g c d ( i , j )    m o d    p \sum_{i=1}^{n}\sum_{j=1}^{n}i \cdot j \cdot gcd(i,j) \; mod \; p i=1nj=1nijgcd(i,j)modp

思路

准备好莫比乌斯反演,开始推式子,枚举 g c d gcd gcd

∑ d = 1 n d 3 ∑ i = 1 ⌊ n d ⌋ ∑ j = 1 ⌊ n d ⌋ i j [ g c d ( i , j ) = 1 ] \sum_{d=1}^{n}d^3 \sum_{i=1}^{\lfloor \frac{n}{d} \rfloor}\sum_{j=1}^{\lfloor \frac{n}{d} \rfloor}ij[gcd(i,j)=1] d=1nd3i=1dnj=1dnij[gcd(i,j)=1]

看到 g c d gcd gcd就可以用 ϵ = I ∗ μ \epsilon = I*\mu ϵ=Iμ

[ g c d ( i , j ) = 1 ] = ∑ d ∣ i , d ∣ j μ ( d ) [gcd(i, j) = 1] = \sum_{d|i,d|j}\mu(d) [gcd(i,j)=1]=di,djμ(d)

所以

∑ d = 1 n d 3 ∑ i = 1 ⌊ n d ⌋ ∑ j = 1 ⌊ n d ⌋ i j ∑ k ∣ i , k ∣ j μ ( k ) \sum_{d=1}^{n}d^3 \sum_{i=1}^{\lfloor \frac{n}{d} \rfloor}\sum_{j=1}^{\lfloor \frac{n}{d} \rfloor}ij\sum_{k|i,k|j}\mu(k) d=1nd3i=1dnj=1dnijki,kjμ(k)

再枚举 k k k

∑ d = 1 n d 3 ∑ k = 1 ⌊ n d ⌋ μ ( k ) ⋅ k 2 ∑ i = 1 ⌊ n d k ⌋ ∑ j = 1 ⌊ n d k ⌋ i j \sum_{d=1}^{n}d^3 \sum_{k=1}^{\lfloor \frac{n}{d} \rfloor}\mu(k)\cdot k^2 \sum_{i=1}^{\lfloor \frac{n}{dk} \rfloor}\sum_{j=1}^{\lfloor \frac{n}{dk} \rfloor}ij d=1nd3k=1dnμ(k)k2i=1dknj=1dknij

后面那两个求和是前缀和,令 s u m ( n ) = ∑ i = 1 n i sum(n)=\sum_{i=1}^{n}i sum(n)=i=1ni,则

∑ d = 1 n d 3 ∑ k = 1 ⌊ n d ⌋ μ ( k ) ⋅ k 2 ⋅ s u m ( ⌊ n d k ⌋ ) 2 \sum_{d=1}^{n}d^3 \sum_{k=1}^{\lfloor \frac{n}{d} \rfloor}\mu(k)\cdot k^2 \cdot sum(\lfloor \frac{n}{dk} \rfloor)^2 d=1nd3k=1dnμ(k)k2sum(dkn)2

看到了两个整除,但是暂时还没有办法分块,所以继续

发现假如我们令 T = d k T=dk T=dk然后枚举 T T T,可以少掉一个求和,多出一个枚举因子,有可能有奇怪的卷积形式出现

∑ T = 1 n s u m ( ⌊ n T ⌋ ) 2 ∑ k ∣ T μ ( k ) ⋅ k 2 ⋅ ( T k ) 3 \sum_{T=1}^{n}sum(\lfloor \frac{n}{T} \rfloor)^2\sum_{k|T}\mu(k) \cdot k^2 \cdot (\frac{T}{k})^3 T=1nsum(Tn)2kTμ(k)k2(kT)3

化简

∑ T = 1 n s u m ( ⌊ n T ⌋ ) 2 ⋅ T 2 ∑ k ∣ T μ ( k ) ⋅ T k \sum_{T=1}^{n}sum(\lfloor \frac{n}{T} \rfloor)^2 \cdot T^2 \sum_{k|T}\mu(k) \cdot \frac{T}{k} T=1nsum(Tn)2T2kTμ(k)kT

最后那个求和真的是一个狄利克雷卷积的形式,而且实际上就是

( μ ∗ I d ) ( T ) = ∑ k ∣ T μ ( k ) ⋅ T k (\mu*Id)(T)=\sum_{k|T}\mu(k) \cdot \frac{T}{k} (μId)(T)=kTμ(k)kT

众所周知(这里不再赘述)

ϕ = μ ∗ I d \phi=\mu*Id ϕ=μId

所以原式就变成了

∑ T = 1 n s u m ( ⌊ n T ⌋ ) 2 ⋅ T 2 ⋅ ϕ ( T ) \sum_{T=1}^{n}sum(\lfloor \frac{n}{T} \rfloor)^2 \cdot T^2 \cdot \phi(T) T=1nsum(Tn)2T2ϕ(T)

F ( T ) = T 2 ⋅ ϕ ( T ) F(T)=T^2 \cdot \phi(T) F(T)=T2ϕ(T),因为 F F F是积性函数,所以可以用杜教筛来算。对于这个式子整除分块,就可以在大概 O ( n 5 6 ) O(n^{\frac{5}{6}}) O(n65)(我也不知道这个复杂度怎么算啊不可能这么大的啊这样的复杂度过不了1e10啊大概杜教筛预处理加上可能跑得还挺快的吧所以还是改成 O ( O( O(能过 ) ) )好了)的时间做出来了。

杜教筛的话我们 另请高明 取

f ( n ) = n 2 ϕ ( n ) , g ( n ) = n 2 f(n)=n^2 \phi(n),g(n)=n^2 f(n)=n2ϕ(n),g(n)=n2

( f ∗ g ) ( n ) = n 3 (f*g)(n)=n^3 (fg)(n)=n3

然后手推二次方前缀和和三次方前缀和,完事。

注意

实际上只要学过一些套路推推式子还是蛮简单的

一定要注意取模, n n n很大但是不能一开始就模掉,所以每次拿来计算的时候一定要取模!!!

代码

#include<bits/stdc++.h> using namespace std; typedef long long LL; const int N = 1e7+10; unordered_map<LL, LL> S; LL n, Sp[N]; LL mod, inv_2, inv_4, inv_6, ans; int p[N], pn; bool vis[N]; inline int fpow(LL x, int y) { LL ret = 1; while (y){ if (y&1) ret = ret * x % mod; x = x * x % mod; y >>= 1; } return ret; } inline void init() { inv_2 = fpow(2, mod-2); inv_4 = fpow(4, mod-2); inv_6 = fpow(6, mod-2); Sp[1] = 1; for (int i = 2; i < N; ++ i){ if (!vis[i]) p[++pn] = i, Sp[i] = 1LL * (i - 1) * i % mod * i % mod; for (int j = 1; j <= pn; ++ j){ if (1LL*i*p[j] >= N) break; vis[i*p[j]] = 1; if (i%p[j] == 0){ Sp[i*p[j]] = Sp[i] * p[j] % mod * p[j] % mod * p[j] % mod; break; } else Sp[i*p[j]] = Sp[i] * (p[j] - 1) % mod * p[j] % mod * p[j] % mod; } } for (int i = 1; i < N; ++ i) (Sp[i] += Sp[i-1]) %= mod; } LL calc_3(LL n){return n * n % mod * (n + 1) % mod * (n + 1) % mod * inv_4 % mod;} LL calc_2(LL n){return n * (n + 1) % mod * (2 * n + 1) % mod * inv_6 % mod;} LL calc_1(LL n){return n * (n + 1) % mod * inv_2 % mod;} // f(n)=n^2*phi(n) g(n)=Id(n)^2 h(n) = Id(n)^3 inline int get_S(LL n) { if (n < N) return Sp[n]; if (S.find(n) != S.end()) return S[n]; LL ret = calc_3(n % mod); for (LL i = 2, j, d; i <= n; i = j+1){ d = n/i; j = n/d; (ret -= (calc_2(j % mod) - calc_2((i - 1) % mod)) * get_S(d) % mod) %= mod; } if (ret < 0) ret += mod; return S[n] = ret; } inline LL solve() { LL ans = 0; for (LL i = 1, j, d; i <= n; i = j+1){ d = n/i; j = n/d; (ans += calc_1(d % mod) * calc_1(d % mod) % mod * (get_S(j) - get_S(i-1)) % mod) %= mod; } return ans < 0 ? ans+mod : ans; } int main() { scanf("%lld%lld", &mod, &n); init(); printf("%lld\n", solve()); return 0; }

最新回复(0)