我爱数论

· · 算法·理论

这篇文章的推导对有着一定的莫反印象,知道莫反的几个基本公式但不知道莫反的解题套路的人很友好,因为相邻两步之间的跨度并不远,认真阅读可以帮助加强对莫反解题套路的熟练度。

有一些题目的最后一步用到了杜教筛,没学过的读者可以自行学习。

[清华集训 2012] 模积和

题意

给定正整数 n,m,求

\sum_{i=1}^n \sum_{j=1}^m (n \bmod i)(m \bmod j),i \neq j

对 19940417 取模的值.

解析

不妨设 n \leq m,并将原式做如下变换:

\begin{aligned} \sum_{i=1}^n \sum_{j=1}^m (n \bmod i)(m \bmod j),i \neq j &= \sum_{i=1}^n \sum_{j=1}^m (n \bmod i)(m \bmod j) - \sum_{i=1}^n (n \bmod i)(m \bmod i) \\ &= \sum_{i=1}^n \sum_{j=1}^m (n- \lfloor \frac{n}{i} \rfloor i)(m - \lfloor \frac{m}{j} \rfloor j) -\sum_{i=1}^n (n- \lfloor \frac{n}{i} \rfloor i)(m- \lfloor \frac{m}{i} \rfloor i) \\ &=\sum_{i=1}^n \sum_{j=1}^m (nm-nj \lfloor \frac{m}{j} \rfloor-mi\lfloor \frac{n}{i} \rfloor+i\lfloor \frac{n}{i} \rfloor j \lfloor \frac{m}{j} \rfloor) - \sum_{i=1}^n(nm-ni \lfloor \frac{m}{i} \rfloor - mi \lfloor \frac{n}{i} \rfloor+i^2 \lfloor \frac{n}{i} \rfloor \lfloor \frac{m}{i} \rfloor) \\ &= n^2m^2-n^2\sum_{j=1}^m j \lfloor \frac{m}{j} \rfloor -m^2 \sum_{i=1}^n i \lfloor \frac{n}{i} \rfloor + \sum_{i=1}^n i \lfloor \frac{n}{i} \rfloor \sum_{j=1}^m j \lfloor \frac{m}{j} \rfloor -n^2m+n \sum_{i=1}^n i \lfloor \frac{m}{i} \rfloor +m\sum_{i=1}^n i \lfloor \frac{n}{i} \rfloor -\sum_{i=1}^n i^2 \lfloor \frac{n}{i} \rfloor \lfloor \frac{m}{i} \rfloor \end{aligned}

设 val_1 = \sum_{i=1}^n i \lfloor \frac{n}{i} \rfloor,val_2 = \sum_{j=1}^m j \lfloor \frac{m}{j} \rfloor,val_3 = \sum_{i=1}^n i \lfloor \frac{m}{i} \rfloor,val_4 = \sum_{i=1}^n i^2 \lfloor \frac{n}{i} \rfloor \lfloor \frac{m}{i} \rfloor,则原式变为

n^2m^2-n^2 \times val_2-m^2 \times val_1+val_1 \times val_2-n^2m+n \times val_3+m \times val_1-val_4 :::success[AC code] ```cpp line-numbers #include<bits/stdc++.h> #define int long long using namespace std; constexpr int mod = 19940417,inv6 = 3323403; int n,m; inline int sum2(const int &l,const int &r){ return (r*(r+1)%mod*(2*r+1)%mod*inv6%mod-(l-1)*l%mod*(2*l-1)%mod*inv6%mod+mod)%mod; } signed main(){ ios::sync_with_stdio(false); cin.tie(0),cout.tie(0); cin >> n >> m; if(n>m){ swap(n,m); } int val1 = 0,val2 = 0,val3 = 0,val4 = 0; for(int l = 1,r = 1;l<=n;l = r+1){ r = min(n,n/(n/l)); val1 = (val1+(r-l+1)*(l+r)/2%mod*(n/l)%mod)%mod; } for(int l = 1,r = 1;l<=m;l = r+1){ r = min(m,m/(m/l)); val2 = (val2+(r-l+1)*(l+r)/2%mod*(m/l)%mod)%mod; } for(int l = 1,r = 1;l<=n;l = r+1){ r = min(n,m/(m/l)); val3 = (val3+(r-l+1)*(l+r)/2%mod*(m/l)%mod)%mod; } for(int l = 1,r = 1;l<=n;l = r+1){ r = min({n,n/(n/l),m/(m/l)}); val4 = (val4+sum2(l,r)*(n/l)%mod*(m/l)%mod)%mod; } cout << (((((((n*n%mod*m%mod*m%mod-n*n%mod*val2%mod+mod)%mod-m*m%mod*val1%mod+mod)%mod+val1*val2%mod)%mod-n*n%mod*m%mod+mod)%mod+n*val3%mod)%mod+m*val1%mod)%mod-val4+mod)%mod; return 0; } ``` ::: ## [[HAOI2011] Problem b](https://www.luogu.com.cn/problem/P2522) ### 题意 $n$ 次询问,每次给定 $a,b,c,d,k$,求 $$ \sum_{x=a}^b \sum_{y=c}^d [\gcd(x,y) = k] $$ ### 解析 设 $f(a,b) = \sum_{x=1}^a \sum_{y=1}^b [\gcd(x,y) = k]$,则原问题答案为 $f(b,d)-f(a-1,d)-f(b,c-1)+f(a-1,c-1)$. 不妨设 $a \leq b$,考虑计算 $f(a,b)$. $$ \begin{aligned} f(a,b) &= \sum_{x=1}^a \sum_{y=1}^b [\gcd(x,y) = k] \\ &= \sum_{x=1}^a [k \mid x] \sum_{y=1}^b [k \mid y] [\gcd(\frac{x}{k},\frac{y}{k}) = 1] \\ &= \sum_{x=1}^{\lfloor \frac{a}{k} \rfloor} \sum_{y=1}^{\lfloor \frac{b}{k} \rfloor} [\gcd(x,y) = 1] \\ &= \sum_{x=1}^{\lfloor \frac{a}{k} \rfloor} \sum_{y=1}^{\lfloor \frac{b}{k} \rfloor} \sum_{d \mid \gcd(x,y)} \mu(d) \\ &= \sum_{d=1}^{\lfloor \frac{a}{k} \rfloor} \mu(d)\sum_{x=1}^{\lfloor \frac{a}{k} \rfloor} [d \mid x] \sum_{y=1}^{\lfloor \frac{b}{k} \rfloor} [d \mid y] \\ &= \sum_{d=1}^{\lfloor \frac{a}{k} \rfloor} \mu(d) \lfloor \frac{\lfloor \frac{a}{k} \rfloor}{d} \rfloor \lfloor \frac{\lfloor \frac{b}{k} \rfloor}{d} \rfloor \end{aligned} $$ 整除分块即可. :::success[AC code] ```cpp line-numbers #include<bits/stdc++.h> #define int long long using namespace std; constexpr int N = 5e4+1; int n,a,b,c,d,k,mu[N]; int primes[N],len = 0; bitset<N>st; void init(){ mu[1] = 1; for(int i = 2;i<N;++i){ if(!st[i]){ primes[++len] = i; mu[i] = -1; } for(int j = 1;j<=len&&i*primes[j]<N;++j){ st[i*primes[j]] = true; if(i%primes[j] == 0){ mu[i*primes[j]] = 0; break; } mu[i*primes[j]] = -mu[i]; } } for(int i = 1;i<N;++i){ mu[i]+=mu[i-1]; } } int f(int a,int b){ int res = 0; for(int l = 1,r = 1;l<=min(a/k,b/k);l = r+1){ r = min({min(a/k,b/k),(a/k)/((a/k)/l),(b/k)/((b/k)/l)}); res+=((a/k)/l)*((b/k)/l)*(mu[r]-mu[l-1]); } return res; } signed main(){ ios::sync_with_stdio(false); cin.tie(0),cout.tie(0); init(); cin >> n; while(n--){ cin >> a >> b >> c >> d >> k; cout << f(b,d)-f(a-1,d)-f(b,c-1)+f(a-1,c-1) << '\n'; } return 0; } ``` ::: ## [[SDOI2015] 约数个数和](https://www.luogu.com.cn/problem/P3327) ### 题意 设 $\tau(x)$ 为 $x$ 的约数个数.$T$ 次询问,每次给定 $n,m$,求 $$ \sum_{i=1}^n \sum_{j=1}^m \tau(ij) $$ ### 解析 对于 $\tau(ij)$,有如下式子: $$ \tau(ij) = \sum_{x \mid i} \sum_{y \mid j} [\gcd(x,y) = 1] $$ 证明略.感性理解吧. 所以根据这个式子,可对原式做莫反之类的东西. 不妨设 $n \leq m$,则 $$ \begin{aligned} \sum_{i=1}^n \sum_{j=1}^m \tau(ij) &= \sum_{i=1}^n \sum_{j=1}^m \sum_{x \mid i} \sum_{y \mid j} [\gcd(x,y) = 1] \\ &= \sum_{i=1}^n \sum_{j=1}^m \sum_{x \mid i} \sum_{y \mid j} \sum_{d \mid \gcd(x,y)} \mu(d) \\ &= \sum_{i=1}^n \sum_{j=1}^m \sum_{d=1}^n \mu(d) (\sum_{d \mid x \mid i} 1)(\sum_{d \mid y \mid j} 1) \end{aligned} $$ 最右面两个式子是等价的,所以我们只考虑 $\sum_{d \mid x \mid i} 1$. 思考这个式子的意义:固定 $d,i$,计数有多少个 $x$ 使得 $d \mid x$ 且 $x \mid i$. 设 $x = k_1d,i = k_2d$.实际上,$k_2 = \frac{i}{d}$.问题等价于计数有多少个 $k_1$ 使得 $k_1 \mid k_2$.我们发现这就是 $\tau(k_2)$. 所以原式可以做如下变换: $$ \begin{aligned} \sum_{i=1}^n \sum_{j=1}^m \sum_{d=1}^n \mu(d) (\sum_{d \mid x \mid i} 1)(\sum_{d \mid y \mid j} 1) &= \sum_{d=1}^n \mu(d) \sum_{i=1}^n [d \mid i]\tau(\frac{i}{d}) \sum_{j=1}^m [d \mid j]\tau(\frac{j}{d})\\ &= \sum_{d=1}^n \mu(d) \sum_{i=1}^{\lfloor \frac{n}{d} \rfloor} \tau(i) \sum_{j=1}^{\lfloor \frac{m}{d} \rfloor} \tau(j) \end{aligned} $$ 我们预处理 $\mu,\tau$ 的前缀和,然后直接整除分块即可。 :::success[AC code] ```cpp line-numbers #include<bits/stdc++.h> #define int long long using namespace std; constexpr int N = 5e4+1; int T,n,m,tau[N]; int primes[N],len = 0; int mu[N]; bitset<N>st; void init(){ mu[1] = tau[1] = 1; for(int i = 2;i<N;++i){ if(!st[i]){ primes[++len] = i; mu[i] = -1; tau[i] = 2; } for(int j = 1;i*primes[j]<N;++j){ st[i*primes[j]] = true; if(i%primes[j] == 0){ int t = i,cnt = 0; while(t%primes[j] == 0){ ++cnt; t/=primes[j]; } tau[i*primes[j]] = tau[t]*(cnt+2); break; } mu[i*primes[j]] = -mu[i]; tau[i*primes[j]] = tau[i]*tau[primes[j]]; } } for(int i = 1;i<N;++i){ mu[i]+=mu[i-1]; tau[i]+=tau[i-1]; } } signed main(){ ios::sync_with_stdio(false); cin.tie(0),cout.tie(0); init(); cin >> T; while(T--){ cin >> n >> m; if(n>m){ swap(n,m); } int res = 0; for(int l = 1,r;l<=n;l = r+1){ r = min({n,n/(n/l),m/(m/l)}); res+=(mu[r]-mu[l-1])*tau[n/l]*tau[m/l]; } cout << res << '\n'; } return 0; } ``` ::: ## [[SDOI2017] 数字表格](https://www.luogu.com.cn/problem/P3704) ### 题意 定义斐波那契数列 $$ f_i= \begin{cases} 0 & i=0 \\ 1 & i=1 \\ f_{i-1}+f_{i-2} & i \geq 2 \end{cases} $$ 求 $$ \prod_{i=1}^n \prod_{j=1}^m f_{\gcd(i,j)} $$ 对 $(10^9+7)$ 取模的值. ### 解析 不妨设 $n \leq m$,考虑计数每一个 $\gcd(i,j)$ 出现的次数. 有 $$ \begin{aligned} \prod_{i=1}^n \prod_{j=1}^m f_{\gcd(i,j)} &= \prod_{g=1}^n f_{g}^{\sum_{i=1}^n \sum_{j=1}^m [\gcd(i,j) = g]} \end{aligned} $$ 指数上的式子就很好处理了。 $$ \begin{aligned} \sum_{i=1}^n \sum_{j=1}^m [\gcd(i,j) = g] &= \sum_{i=1}^n [g \mid i] \sum_{j=1}^m [g \mid j] [\gcd(\frac{i}{g},\frac{j}{g}) = 1] \\ &= \sum_{i=1}^{\lfloor \frac{n}{g} \rfloor} \sum_{j=1}^{\lfloor \frac{m}{g} \rfloor} [\gcd(i,j) = 1] \\ &= \sum_{i=1}^{\lfloor \frac{n}{g} \rfloor} \sum_{j=1}^{\lfloor \frac{m}{g} \rfloor} \sum_{d \mid \gcd(i,j)} \mu(d) \\ &= \sum_{d=1}^{\lfloor \frac{n}{g} \rfloor}\mu(d)\sum_{i=1}^{\lfloor \frac{n}{g} \rfloor} [d \mid i] \sum_{j=1}^{\lfloor \frac{m}{g} \rfloor} [d \mid j] \\ &= \sum_{d=1}^{\lfloor \frac{n}{g} \rfloor}\mu(d) \lfloor \frac{n}{dg} \rfloor \lfloor \frac{m}{dg} \rfloor \end{aligned} $$ 代回原式,有 $$ \begin{aligned} \prod_{g=1}^n f_{g}^{\sum_{i=1}^n \sum_{j=1}^m [\gcd(i,j) = g]} &= \prod_{g=1}^n f_{g}^{\sum_{d=1}^{\lfloor \frac{n}{g} \rfloor}\mu(d) \lfloor \frac{n}{dg} \rfloor \lfloor \frac{m}{dg} \rfloor} \\ &= \prod_{g=1}^n \prod_{d=1}^{\lfloor \frac{n}{g} \rfloor} f_g^{\mu(d) \lfloor \frac{n}{dg} \rfloor \lfloor \frac{m}{dg} \rfloor} \end{aligned} $$ 转而枚举 $T = dg$,有 $$ \prod_{g=1}^n \prod_{d=1}^{\lfloor \frac{n}{g} \rfloor} f_g^{\mu(d) \lfloor \frac{n}{dg} \rfloor \lfloor \frac{m}{dg} \rfloor}=\prod_{T=1}^n \prod_{g \mid T}f_g^{\mu(\frac{T}{g}) \lfloor \frac{n}{T} \rfloor \lfloor \frac{m}{T} \rfloor} $$ 设 $F(T) = \prod_{g \mid T} f_g^{\mu(\frac{T}{g})}$,则答案为 $$ \prod_{T=1}^n F^{\lfloor \frac{n}{T} \rfloor \lfloor \frac{m}{T} \rfloor}(T) $$ 我们发现 $F(T)$ 可以 $O(n \ln n)$ 预处理,所以这题直接整除分块即可.时间复杂度为 $O(n \ln n + q \sqrt n \log n)$。 代码中给出的实现在预处理 $F$ 时重复调用了 ``qpow``,所以代码中的实现是 $O(n \log n \log mod+q \sqrt n \log mod)$ 的,不过依然能过就是了。 :::success[AC code] ```cpp line-numbers #include<bits/stdc++.h> #define int long long using namespace std; constexpr int N = 1e6+1,mod = 1e9+7; int T,n,m,f[N],mu[N],F[N],pre[N],inv[N]; int primes[N],len = 0; bitset<N>st; inline int qpow(int base,int e){ int res = 1; while(e){ if(e&1){ res = res*base%mod; } base = base*base%mod; e>>=1; } return res; } void init(){ f[0] = 0,f[1] = 1; for(int i = 2;i<N;++i){ f[i] = (f[i-1]+f[i-2])%mod; } mu[1] = 1; for(int i = 2;i<N;++i){ if(!st[i]){ primes[++len] = i; mu[i] = -1; } for(int j = 1;i*primes[j]<N;++j){ st[i*primes[j]] = true; if(i%primes[j] == 0){ break; } mu[i*primes[j]] = -mu[i]; } } for(int i = 1;i<N;++i){ F[i] = 1; } for(int g = 1;g<N;++g){ for(int T = g;T<N;T+=g){ int val = mu[T/g]; if(val == 1){ F[T] = F[T]*f[g]%mod; }else if(val == -1){ F[T] = F[T]*qpow(f[g],mod-2)%mod; } } } pre[0] = 1; for(int i = 1;i<N;++i){ pre[i] = pre[i-1]*F[i]%mod; } inv[N-1] = qpow(pre[N-1],mod-2); for(int i = N-2;i>=0;--i){ inv[i] = inv[i+1]*F[i+1]%mod; } } signed main(){ ios::sync_with_stdio(false); cin.tie(0),cout.tie(0); init(); cin >> T; while(T--){ cin >> n >> m; if(n>m){ swap(n,m); } int res = 1; for(int l = 1,r;l<=n;l = r+1){ r = min({n,n/(n/l),m/(m/l)}); res = res*qpow(pre[r]%mod*inv[l-1]%mod,(n/l)*(m/l))%mod; } cout << res << '\n'; } return 0; } ``` ::: ## [[SDOI2014] 数表](https://www.luogu.com.cn/problem/P3312) ### 题意 $Q$ 次询问,每次给定 $n,m,a$,计算 $$ \sum_{i=1}^n \sum_{j=1}^m [\sigma(\gcd(i,j)) \leq a] \sigma(\gcd(i,j)) $$ 对 $2^{31}$ 取模的值。 ### 解析 不妨设 $n \leq m$,并将原式套路地转化: $$ \begin{aligned} \sum_{i=1}^n \sum_{j=1}^m [\sigma(\gcd(i,j)) \leq a] \sigma(\gcd(i,j)) &= \sum_{g=1}^n \sum_{i=1}^n \sum_{j=1}^m [\gcd(i,j) = g] [\sigma(g) \leq a] \sigma(g) \\ &= \sum_{g=1}^n [\sigma(g) \leq a] \sigma(g) \sum_{i=1}^{\lfloor \frac{n}{g} \rfloor} \sum_{j=1}^{\lfloor \frac{m}{g} \rfloor} [\gcd(i,j) = 1] \\ &= \sum_{g=1}^n [\sigma(g) \leq a] \sigma(g) \sum_{i=1}^{\lfloor \frac{n}{g} \rfloor} \sum_{j=1}^{\lfloor \frac{m}{g} \rfloor} \sum_{d \mid \gcd(i,j)} \mu(d) \\ &= \sum_{g=1}^n [\sigma(g) \leq a] \sigma(g) \sum_{d=1}^{\lfloor \frac{n}{g} \rfloor} \mu(d) \lfloor \frac{n}{dg} \rfloor \lfloor \frac{m}{dg} \rfloor \\ &= \sum_{T=1}^n \sum_{g \mid T} [\sigma(g) \leq a] \sigma(g) \mu(\frac{T}{g}) \lfloor \frac{n}{T} \rfloor \lfloor \frac{m}{T} \rfloor \\ \end{aligned} $$ 设 $F(T) = \sum_{g \mid T} [\sigma(g) \leq a] \sigma(g) \mu(\frac{T}{g})$,可将原式化为: $$ \sum_{T=1}^n F(T) \lfloor \frac{n}{T} \rfloor \lfloor \frac{m}{T} \rfloor $$ 考虑如何计算 $F(T)$。 一个很显然的思路是把所有的询问都离线下来,按 $a$ 升序排序。所以我们可以对每一个 $\sigma(g)$ 在 $(a_{i-1},a_i]$ 之间的 $g$ 枚举倍数 $T$ 然后直接加,用树状数组维护即可(单点加,区间求和)。但是“枚举合法的 $g$”这一步是 $O(n)$ 的,这会导致崩溃,我们需要更优的方法。 我们发现我们可以将所有二元组 $(\sigma(g),g)$ 按第一维升序排序,然后我们维护一个指针就好了。最后是一个树状数组单点加。时间复杂度 $O(Q \sqrt n \log n + n \log^2 n)$。 :::success[AC code] ```cpp line-numbers #include<bits/stdc++.h> #define int long long using namespace std; constexpr int N = 1e5+1,mod = 1ll<<31; int T,mu[N],sigma[N],pe[N],ans[N]; struct node{ int n,m,a,id; }que[N]; pair<int,int>pii[N]; inline bool cmp(const node &x,const node &y){ return x.a<y.a; } int primes[N],len = 0; bitset<N>st; void init(){ mu[1] = sigma[1] = pe[1] = 1; for(int i = 2;i<N;++i){ if(!st[i]){ primes[++len] = i; mu[i] = -1; sigma[i] = pe[i] = i+1; } for(int j = 1;i*primes[j]<N;++j){ st[i*primes[j]] = true; if(i%primes[j] == 0){ pe[i*primes[j]] = pe[i]*primes[j]+1; sigma[i*primes[j]] = sigma[i]/pe[i]*pe[i*primes[j]]; break; } pe[i*primes[j]] = primes[j]+1; sigma[i*primes[j]] = sigma[i]*sigma[primes[j]]; mu[i*primes[j]] = -mu[i]; } } } namespace BIT{ int tree[N]; inline int lowbit(const int &x){ return x&-x; } inline void add(const int &x,const int &y){ for(int i = x;i<N;i+=lowbit(i)){ tree[i] = (tree[i]+y)%mod; } } inline int query(const int &x){ int sum = 0; for(int i = x;i;i^=lowbit(i)){ sum = (sum+tree[i])%mod; } return sum; } }using namespace BIT; signed main(){ ios::sync_with_stdio(false); cin.tie(0),cout.tie(0); init(); for(int i = 1;i<N;++i){ pii[i].first = sigma[i]; pii[i].second = i; } sort(pii+1,pii+N); cin >> T; for(int i = 1;i<=T;++i){ cin >> que[i].n >> que[i].m >> que[i].a; que[i].id = i; } sort(que+1,que+T+1,cmp); int pos = 0; for(int i = 1;i<=T;++i){ while(pos<N-1&&pii[pos+1].first<=que[i].a){ ++pos; for(int j = pii[pos].second;j<N;j+=pii[pos].second){ add(j,(pii[pos].first%mod*mu[j/pii[pos].second]%mod+mod)%mod); } } if(que[i].n>que[i].m){ swap(que[i].n,que[i].m); } int res = 0; for(int l = 1,r;l<=que[i].n;l = r+1){ r = min(que[i].n/(que[i].n/l),que[i].m/(que[i].m/l)); res = (res+(que[i].n/l)*(que[i].m/l)%mod*((query(r)-query(l-1)+mod)%mod)%mod)%mod; } ans[que[i].id] = res; } for(int i = 1;i<=T;++i){ cout << ans[i] << '\n'; } return 0; } ``` ::: ## [P3768 简单的数学题](https://www.luogu.com.cn/problem/P3768) ### 题意 给定正整数 $n,p$,求 $$ \sum_{i=1}^n \sum_{j=1}^n ij \gcd(i,j) $$ 对 $p$ 取模的值。 $1 \leq n \leq 10^{10},1 \leq p \leq 1.1 \times 10^9$ 且 $p$ 为质数。 ### 解析 我们套路地对原式转化: $$ \begin{aligned} \sum_{i=1}^n \sum_{j=1}^n ij \gcd(i,j) &= \sum_{g=1}^n g\sum_{i=1}^n i\sum_{j=1}^n j [\gcd(i,j) = g] \\ &= \sum_{g=1}^n g\sum_{i=1}^n i [g \mid i]\sum_{j=1}^n j [g \mid j][\gcd(\frac{i}{g},\frac{j}{g}) = 1] \\ &= \sum_{g=1}^n g\sum_{i=1}^{\lfloor \frac{n}{g} \rfloor} ig \sum_{j=1}^{\lfloor \frac{n}{g} \rfloor} jg [\gcd(i,j) = 1] \\ &= \sum_{g=1}^n g^3 \sum_{i=1}^{\lfloor \frac{n}{g} \rfloor} i \sum_{j=1}^{\lfloor \frac{n}{g} \rfloor} j \sum_{d \mid \gcd(i,j)} \mu(d) \\ &= \sum_{g=1}^n g^3 \sum_{d=1}^{\lfloor \frac{n}{g} \rfloor} \mu(d) \sum_{i=1}^{\lfloor \frac{n}{g} \rfloor} i [d \mid i] \sum_{j=1}^{\lfloor \frac{n}{g} \rfloor} j [d \mid j] \\ &= \sum_{g=1}^n g^3 \sum_{d=1}^{\lfloor \frac{n}{g} \rfloor} \mu(d) d^2(\sum_{i=1}^{\lfloor \frac{n}{dg} \rfloor} i)^2 \\ &= \sum_{g=1}^n g^3 \sum_{d=1}^{\lfloor \frac{n}{g} \rfloor} \mu(d) d^2(\frac{(1+\lfloor \frac{n}{dg} \rfloor) \lfloor \frac{n}{dg} \rfloor}{2})^2 \\ &= \sum_{T=1}^n \sum_{g \mid T} g^3 \mu(\frac{T}{g}) (\frac{T}{g})^2 (\frac{(1+\lfloor \frac{n}{T} \rfloor) \lfloor \frac{n}{T} \rfloor}{2})^2 \\ &= \sum_{T=1}^n (\frac{(1+\lfloor \frac{n}{T} \rfloor) \lfloor \frac{n}{T} \rfloor}{2})^2 T^2 \sum_{g \mid T} g\mu(\frac{T}{g}) \\ &= \sum_{T=1}^n (\frac{(1+\lfloor \frac{n}{T} \rfloor) \lfloor \frac{n}{T} \rfloor}{2})^2 T^2 \varphi(T) \end{aligned} $$ 然后杜教筛优化即可。 令 $f(n) = n^2 \varphi(n)$,取 $g = id_2$,可得 $h = f*g = id_3$,即 $$ (f*g)(n) = \sum_{d \mid n} d^2 \varphi(d) (\frac{n}{d})^2 = n^2 \sum_{d \mid n} \varphi(d) = n^3 = id_3(n) = h(n) $$ $g,h$ 都是可以 $O(1)$ 计算前缀和的。所以这题就做完了。 :::success[AC code] ```cpp line-numbers #include<bits/stdc++.h> #define ll long long using namespace std; constexpr int N = 4641589; ll mod,n,inv2,inv6; int phi[N]; ll F[N]; int primes[N],len = 0; bitset<N>st; inline ll qpow(int base,ll e){ ll res = 1; while(e){ if(e&1){ res = res*base%mod; } base = 1ll*base*base%mod; e>>=1; } return res; } void init(){ phi[1] = 1; for(int i = 2;i<N;++i){ if(!st[i]){ primes[++len] = i; phi[i] = (i-1)%mod; } for(int j = 1;i*primes[j]<N;++j){ st[i*primes[j]] = true; if(i%primes[j] == 0){ phi[i*primes[j]] = phi[i]*primes[j]%mod; break; } phi[i*primes[j]] = phi[i]*phi[primes[j]]%mod; } } for(int i = 1;i<N;++i){ F[i] = i%mod*i%mod*phi[i]%mod; F[i] = (F[i]+F[i-1])%mod; } } inline ll calc1(ll n){ return n%mod*(n+1)%mod*inv2%mod; } inline ll calc2(ll n){ return n%mod*(n+1)%mod*(2*n%mod+1)%mod*inv6%mod; } map<ll,ll>mp; ll SumF(ll n){ if(n<N){ return F[n]; }else if(mp.count(n)){ return mp[n]; } ll ans = calc1(n)*calc1(n)%mod; for(ll l = 2,r;l<=n;l = r+1){ r = n/(n/l); ans = (ans-(calc2(r)-calc2(l-1)+mod)%mod*SumF(n/l)%mod+mod)%mod; } return mp[n] = ans; } signed main(){ ios::sync_with_stdio(false); cin.tie(0),cout.tie(0); cin >> mod >> n; inv2 = qpow(2,mod-2); inv6 = qpow(6,mod-2); init(); ll ans = 0; for(ll l = 1,r;l<=n;l = r+1){ r = n/(n/l); ans = (ans+calc1(n/l)*calc1(n/l)%mod*((SumF(r)-SumF(l-1)+mod)%mod)%mod)%mod; } cout << ans; return 0; } ``` ::: ## [P2424 约数和](https://www.luogu.com.cn/problem/P2424) ### 题意 给定正整数 $x,y$,计算 $$ \sum_{i=x}^y \sigma(i) $$ ### 解析 我们首先把这个东西拆成差分,这样下标就从 $1$ 开始了。 只需计算 $f(n) = \sum_{i=1}^n \sigma(i)$。 我们对 $f(n)$ 做如下变换: $$ \begin{aligned} \sum_{i=1}^n \sigma(i) &= \sum_{i=1}^n \sum_{d \mid i} d \\ &= \sum_{d=1}^n d \sum_{d \mid i,i \leq n} 1\\ &= \sum_{d=1}^n d \lfloor \frac{n}{d} \rfloor \end{aligned} $$ :::success[AC code] ```cpp line-numbers #include<bits/stdc++.h> #define ll long long using namespace std; int x,y; ll f(int n){ ll ans = 0; for(ll l = 1,r;l<=n;l = r+1){ r = n/(n/l); ans+=(l+r)*(r-l+1)/2*(n/l); } return ans; } signed main(){ ios::sync_with_stdio(false); cin.tie(0),cout.tie(0); cin >> x >> y; cout << f(y)-f(x-1); return 0; } ``` ::: ## [[POI 2007] ZAP-Queries](https://www.luogu.com.cn/problem/P3455) ### 题意 给定正整数 $a,b,g,g \leq a,b$,计算 $$ \sum_{i=1}^a \sum_{j=1}^b [\gcd(i,j) = g] $$ ### 解析 不妨设 $a \leq b$,套路地对原式转化: $$ \begin{aligned} \sum_{i=1}^a \sum_{j=1}^b [\gcd(i,j) = g] &= \sum_{i=1}^a [g \mid i] \sum_{j=1}^b [g \mid j] [\gcd(\frac{i}{g},\frac{j}{g}) = 1] \\ &= \sum_{i=1}^{\lfloor \frac{a}{g} \rfloor} \sum_{j=1}^{\lfloor \frac{b}{g} \rfloor} [\gcd(i,j) = 1] \\ &= \sum_{i=1}^{\lfloor \frac{a}{g} \rfloor} \sum_{j=1}^{\lfloor \frac{b}{g} \rfloor} \sum_{d \mid \gcd(i,j)} \mu(d) \\ &= \sum_{d=1}^{\lfloor \frac{a}{g} \rfloor} \mu(d) \sum_{i=1}^{\lfloor \frac{a}{g} \rfloor} [d \mid i] \sum_{j=1}^{\lfloor \frac{b}{g} \rfloor} [d \mid j] \\ &= \sum_{d=1}^{\lfloor \frac{a}{g} \rfloor} \mu(d) \lfloor \frac{\lfloor \frac{a}{g} \rfloor}{d} \rfloor \lfloor \frac{\lfloor \frac{b}{g} \rfloor}{d} \rfloor \end{aligned} $$ 然后数论分块即可。 :::success[AC code] ```cpp line-numbers #include<bits/stdc++.h> #define ll long long using namespace std; constexpr int N = 5e4+1; int T,a,b,g; int mu[N]; ll premu[N]; int primes[N],len = 0; bitset<N>st; void init(){ mu[1] = 1; for(int i = 2;i<N;++i){ if(!st[i]){ primes[++len] = i; mu[i] = -1; } for(int j = 1;i*primes[j]<N;++j){ st[i*primes[j]] = true; if(i%primes[j] == 0){ break; } mu[i*primes[j]] = -mu[i]; } } for(int i = 1;i<N;++i){ premu[i] = premu[i-1]+mu[i]; } } signed main(){ ios::sync_with_stdio(false); cin.tie(0),cout.tie(0); init(); cin >> T; while(T--){ cin >> a >> b >> g; if(a>b){ swap(a,b); } ll res = 0,n = a/g,m = b/g; for(ll l = 1,r;l<=n;l = r+1){ r = min(n/(n/l),m/(m/l)); res+=(premu[r]-premu[l-1])*(n/l)*(m/l); } cout << res << '\n'; } return 0; } ``` ::: ## [P5221 Product](https://www.luogu.com.cn/problem/P5221) ### 题意 给定正整数 $n$,计算 $$ \prod_{i=1}^n \prod_{j=1}^n \frac{\operatorname{lcm}(i,j)}{\gcd(i,j)} $$ 对 $104857601$ 取模,一个质数。 ### 题解 我们对原式做如下转化: $$ \begin{aligned} \prod_{i=1}^n \prod_{j=1}^n \frac{\operatorname{lcm}(i,j)}{\gcd(i,j)} &= \prod_{i=1}^n \prod_{j=1}^n \frac{ij}{\gcd^2(i,j)} \\ &= \frac{\prod_{i=1}^n \prod_{j=1}^n ij}{\prod_{g=1}^n (g^2)^{\sum_{i=1}^n \sum_{j=1}^n [\gcd(i,j) = g]}} \\ &= \frac{(n!)^{2n}}{\prod_{g=1}^n (g^2)^{\sum_{d=1}^{\lfloor \frac{n}{g} \rfloor} \mu(d) \lfloor \frac{n}{dg} \rfloor^2}} \\ &= \frac{(n!)^{2n}}{\prod_{T=1}^n \prod_{g \mid T} (g^2)^{\mu(\frac{T}{g}) \lfloor \frac{n}{T} \rfloor^2}} \end{aligned} $$ 令 $F(T) = \prod_{g \mid T} (g^2)^{\mu(\frac{T}{g})}$,则原式变为: $$ \frac{(n!)^{2n}}{\prod_{T=1}^n \prod_{g \mid T} (g^2)^{\mu(\frac{T}{g}) \lfloor \frac{n}{T} \rfloor^2}} = \frac{(n!)^{2n}}{\prod_{T=1}^n F^{\lfloor \frac{n}{T} \rfloor^2}(T)} $$ 我们发现 $F(T)$ 可以 $O(n \ln n)$ 预处理,所以这题就做完了。 注意卡常。 :::success[AC code] ```cpp line-numbers #include<bits/stdc++.h> #define ll long long using namespace std; constexpr int N = 1e6+1,mod = 104857601; int n; inline int qpow(int base,int e){ int res = 1; while(e){ if(e&1){ res = 1ll*res*base%mod; } base = 1ll*base*base%mod; e>>=1; } return res; } int mu[N],F[N],preF[N],invF[N],inv[N]; int primes[N],len = 0; bitset<N>st; void init(){ mu[1] = inv[1] = 1; for(int i = 2;i<=n;++i){ inv[i] = 1ll*(mod-mod/i)*inv[mod%i]%mod; } for(int i = 2;i<=n;++i){ if(!st[i]){ primes[++len] = i; mu[i] = -1; } for(int j = 1;i*primes[j]<=n;++j){ st[i*primes[j]] = true; if(i%primes[j] == 0){ break; } mu[i*primes[j]] = -mu[i]; } } for(int i = 1;i<=n;++i){ F[i] = 1; } for(ll g = 1;g<=n;++g){ for(int T = g;T<=n;T+=g){ if(mu[T/g] == 1){ F[T] = 1ll*F[T]*g%mod*g%mod; }else if(mu[T/g] == -1){ F[T] = 1ll*F[T]*inv[g]%mod*inv[g]%mod; } } } preF[0] = 1; for(int i = 1;i<=n;++i){ preF[i] = 1ll*preF[i-1]*F[i]%mod; } invF[n] = qpow(preF[n],mod-2); for(int i = n-1;i>=0;--i){ invF[i] = 1ll*invF[i+1]*F[i+1]%mod; } } signed main(){ ios::sync_with_stdio(false); cin.tie(0),cout.tie(0); cin >> n; init(); int ans = 1; for(int i = 1;i<=n;++i){ ans = 1ll*ans*i%mod; } ans = qpow(ans,2*n); for(int l = 1,r;l<=n;l = r+1){ r = n/(n/l); ans = 1ll*ans*qpow(qpow(1ll*preF[r]*invF[l-1]%mod,1ll*(n/l)*(n/l)%(mod-1)),mod-2)%mod; } cout << ans; return 0; } ``` ::: ## [P4450 双亲数](https://www.luogu.com.cn/problem/P4450) 同 [[POI 2007] ZAP-Queries](https://www.luogu.com.cn/problem/P3455)。 ## [[集训队互测 2010] Crash的数字表格](https://www.luogu.com.cn/problem/P1829) ### 题意 给定正整数 $n,m$,计算 $$ \sum_{i=1}^n \sum_{j=1}^m \operatorname{lcm}(i,j) $$ 对 $20101009$ 取模的值。 ### 解析 不妨设 $n \leq m$,并套路地对原式转化: $$ \begin{aligned} \sum_{i=1}^n \sum_{j=1}^m \operatorname{lcm}(i,j) &= \sum_{i=1}^n \sum_{j=1}^m \frac{ij}{\gcd(i,j)} \\ &= \sum_{g=1}^n \frac{1}{g} \sum_{i=1}^n i \sum_{j=1}^m j [\gcd(i,j) = g] \\ &= \sum_{g=1}^n \frac{1}{g} \sum_{i=1}^n i [g \mid i] \sum_{j=1}^m j [g \mid j] [\gcd(\frac{i}{g},\frac{j}{g}) = 1] \\ &= \sum_{g=1}^n g \sum_{i=1}^{\lfloor \frac{n}{g} \rfloor} i \sum_{j=1}^{\lfloor \frac{m}{g} \rfloor} j [\gcd(i,j) = 1] \\ &= \sum_{g=1}^n g \sum_{i=1}^{\lfloor \frac{n}{g} \rfloor} i \sum_{j=1}^{\lfloor \frac{m}{g} \rfloor} j \sum_{d \mid \gcd(i,j)} \mu(d) \\ &= \sum_{g=1}^n g \sum_{d=1}^{\lfloor \frac{n}{g} \rfloor} \mu(d) \sum_{i=1}^{\lfloor \frac{n}{g} \rfloor} i [d \mid i] \sum_{j=1}^{\lfloor \frac{m}{g} \rfloor} j [d \mid j] \\ &= \sum_{g=1}^n g \sum_{d=1}^{\lfloor \frac{n}{g} \rfloor} \mu(d) d^2 \frac{(1+\lfloor \frac{n}{dg} \rfloor) \lfloor \frac{n}{dg} \rfloor}{2} \cdot \frac{(1+\lfloor \frac{m}{dg} \rfloor) \lfloor \frac{m}{dg} \rfloor}{2} \\ &= \sum_{T=1}^n \sum_{g \mid T} g \mu(\frac{T}{g}) (\frac{T}{g})^2 \frac{(1+\lfloor \frac{n}{T} \rfloor) \lfloor \frac{n}{T} \rfloor}{2} \cdot \frac{(1+\lfloor \frac{m}{T} \rfloor) \lfloor \frac{m}{T} \rfloor}{2} \\ &= \sum_{T=1}^n \frac{(1+\lfloor \frac{n}{T} \rfloor) \lfloor \frac{n}{T} \rfloor}{2} \cdot \frac{(1+\lfloor \frac{m}{T} \rfloor) \lfloor \frac{m}{T} \rfloor}{2} \cdot T^2 \sum_{g \mid T} \mu(\frac{T}{g}) \frac{1}{g} \\ &= \sum_{T=1}^n \frac{(1+\lfloor \frac{n}{T} \rfloor) \lfloor \frac{n}{T} \rfloor}{2} \cdot \frac{(1+\lfloor \frac{m}{T} \rfloor) \lfloor \frac{m}{T} \rfloor}{2} \cdot T^2 \sum_{g \mid T} \mu(g) \frac{1}{\frac{T}{g}} \\ &= \sum_{T=1}^n \frac{(1+\lfloor \frac{n}{T} \rfloor) \lfloor \frac{n}{T} \rfloor}{2} \cdot \frac{(1+\lfloor \frac{m}{T} \rfloor) \lfloor \frac{m}{T} \rfloor}{2} \cdot T \sum_{g \mid T} \mu(g) g \end{aligned} $$ 函数 $F(T) = \sum_{g \mid T} \mu(g) g$ 可以线性预处理,其是一个积性函数。 具体地: - $F(1) = 1

:::success[AC code]

#include<bits/stdc++.h>
#define ll long long
using namespace std;
constexpr int N = 1e7+1,mod = 20101009;
int n,m;
int inv[N],F[N];
int primes[N],len = 0;
bitset<N>st;
void init(){
    inv[1] = F[1] = 1;
    for(int i = 2;i<=max(m,6);++i){
        inv[i] = 1ll*(mod-mod/i)*inv[mod%i]%mod;
    }
    for(int i = 2;i<=m;++i){
        if(!st[i]){
            primes[++len] = i;
            F[i] = (1-i+mod)%mod;
        }
        for(int j = 1;i*primes[j]<=m;++j){
            st[i*primes[j]] = true;
            if(i%primes[j] == 0){
                F[i*primes[j]] = F[i];
                break;
            }
            F[i*primes[j]] = 1ll*F[i]*F[primes[j]]%mod;
        }
    }
    for(int i = 1;i<=m;++i){
        F[i] = 1ll*F[i]*i%mod;
        F[i] = (F[i]+F[i-1])%mod;
    }
}
inline int calc1(int n){
    return 1ll*n*(n+1)%mod*inv[2]%mod;
}
inline int calc2(int n){
    return 1ll*n*(n+1)%mod*(2*n+1)%mod*inv[6]%mod;
}
signed main(){
    ios::sync_with_stdio(false);
    cin.tie(0),cout.tie(0);
    cin >> n >> m;
    if(n>m){
        swap(n,m);
    }
    init();
    int res = 0;
    for(int l = 1,r;l<=n;l = r+1){
        r = min(n/(n/l),m/(m/l));
        res = (res+1ll*calc1(n/l)*calc1(m/l)%mod*((F[r]-F[l-1]+mod)%mod)%mod)%mod;
    }
    cout << res;
    return 0;
}

:::

P4449 于神之怒加强版

题意

给定 k,有 T 次询问,每次询问给定 n,m,计算

\sum_{i=1}^n \sum_{j=1}^m \gcd(i,j)^k

对 10^9+7 取模的值。

解析

不妨设 n \leq m,并套路地对原式转化:

\begin{aligned} \sum_{i=1}^n \sum_{j=1}^m \gcd(i,j)^k &= \sum_{g=1}^n g^k \sum_{i=1}^n \sum_{j=1}^m [\gcd(i,j) = g] \\ &= \sum_{g=1}^n g^k \sum_{i=1}^{\lfloor \frac{n}{g} \rfloor} \sum_{j=1}^{\lfloor \frac{m}{g} \rfloor} [\gcd(i,j) = 1] \\ &= \sum_{g=1}^n g^k \sum_{i=1}^{\lfloor \frac{n}{g} \rfloor} \sum_{j=1}^{\lfloor \frac{m}{g} \rfloor} \sum_{d \mid \gcd(i,j)} \mu(d) \\ &= \sum_{g=1}^n g^k \sum_{d=1}^{\lfloor \frac{n}{g} \rfloor} \mu(d) \lfloor \frac{n}{dg} \rfloor \lfloor \frac{m}{dg} \rfloor \\ &= \sum_{T=1}^n \lfloor \frac{n}{T} \rfloor \lfloor \frac{m}{T} \rfloor \sum_{g \mid T} g^k \mu(\frac{T}{g}) \end{aligned}

这题能 O(n \ln n),就不继续优化了。

:::success[AC code]

#include<bits/stdc++.h>
#define ll long long
using namespace std;
constexpr int N = 5e6+1,mod = 1e9+7;
int t,k,n,m;
int primes[N],len = 0;
int mu[N],idk[N],F[N];
bitset<N>st;
inline int qpow(int base,int e){
    int res = 1;
    while(e){
        if(e&1){
            res = 1ll*res*base%mod;
        }
        base = 1ll*base*base%mod;
        e>>=1;
    }
    return res;
}
void init(){
    mu[1] = idk[1] = 1;
    for(int i = 2;i<N;++i){
        if(!st[i]){
            primes[++len] = i;
            mu[i] = mod-1;
            idk[i] = qpow(i,k);
        }
        for(int j = 1;i*primes[j]<N;++j){
            st[i*primes[j]] = true;
            idk[i*primes[j]] = 1ll*idk[i]*idk[primes[j]]%mod;
            if(i%primes[j] == 0){
                break;
            }
            mu[i*primes[j]] = (-mu[i]+mod)%mod;
        }
    }
    for(int g = 1;g<N;++g){
        for(int T = g;T<N;T+=g){
            F[T] = (F[T]+1ll*idk[g]*mu[T/g]%mod+mod)%mod;
        }
    }
    for(int i = 1;i<N;++i){
        F[i] = (F[i]+F[i-1])%mod;
    }
}
signed main(){
    ios::sync_with_stdio(false);
    cin.tie(0),cout.tie(0);
    cin >> t >> k;
    init();
    while(t--){
        cin >> n >> m;
        if(n>m){
            swap(n,m);
        }
        int res = 0;
        for(int l = 1,r;l<=n;l = r+1){
            r = min(n/(n/l),m/(m/l));
            res = (res+1ll*(n/l)*(m/l)%mod*((F[r]-F[l-1]+mod)%mod)%mod)%mod;
        }
        cout << res << '\n';
    }
    return 0;
}

:::

P4917 天守阁的地板

题意

$$ \prod_{i=1}^n \prod_{j=1}^n \frac{\operatorname{lcm}(i,j)^2}{ij} $$ 对 $19260817$ 取模的值。 ### 解析 $$ \prod_{i=1}^n \prod_{j=1}^n \frac{\operatorname{lcm}(i,j)^2}{ij}= \prod_{i=1}^n \prod_{j=1}^n \frac{ij}{\gcd(i,j)^2} \\ $$ 然后同 [P5221 Product](https://www.luogu.com.cn/problem/P5221)。