bzoj 4176
题意:求$\sum_{i=1}^{n}\sum_{j=1}^{n}d(ij)$
首先推一发式子:
$\sum_{i=1}^{n}\sum_{j=1}^{n}d(ij)$
有一个结论:$d(nm)=\sum_{i|n}\sum_{j|m}[gcd(i,j)\equiv 1]$
然后代入,得:
$\sum_{i=1}^{n}\sum_{j=1}^{n}\sum_{p|i}\sum_{q|j}[gcd(p,q)\equiv 1]$
然后优先枚举$p$,$q$,得到:
$\sum_{p=1}^{n}\sum_{q=1}^{n}[gcd(p,q)\equiv 1]\sum_{p|i}\sum_{q|j}1$
那么也就是:
$\sum_{p=1}^{n}\sum_{q=1}^{n}[gcd(p,q)\equiv 1]\frac{n}{p}\frac{n}{q}$
接下来就是常规步骤了
$\sum_{p=1}^{n}\sum_{q=1}^{n}\sum_{t|gcd(p,q)}\mu(t)\frac{n}{p}\frac{n}{p}$
也就是:
$\sum_{t=1}^{n}\mu(t)\sum_{p=1}^{\frac{n}{t}}\frac{n}{pt}\sum_{q=1}^{\frac{n}{t}}\frac{n}{qt}$
设$f(n)=\sum_{i=1}^{n}\frac{n}{i}$,那么后面就可以变成$\sum_{t=1}^{n}\mu(t)f(\frac{n}{t})^{2}$
求$f$可以数论分块,用杜教筛筛出$\mu$的前缀和之后再套个数论分块即可
代码:
#include <cstdio> #include <cmath> #include <cstring> #include <cstdlib> #include <iostream> #include <algorithm> #include <queue> #include <stack> #include <map> #define ll long long using namespace std; const ll mode=1000000007; map <ll,ll> S; int mu[10000005]; ll s[10000005]; int pri[10000005]; int cnt=0; bool used[10000005]; void init() { mu[1]=s[1]=1; for(int i=2;i<=10000000;i++) { if(!used[i])pri[++cnt]=i,mu[i]=-1; for(int j=1;j<=cnt&&i*pri[j]<=10000000;j++) { used[i*pri[j]]=1; if(i%pri[j]==0){mu[i*pri[j]]=0;break;} mu[i*pri[j]]=-mu[i]; } s[i]=(s[i-1]+mu[i]+mode)%mode; } } ll get_S(ll x) { if(x<=10000000)return s[x]; else if(S.find(x)!=S.end())return S[x]; ll ret=0; int las=0; for(int i=2;i<=x;i=las+1) { las=x/(x/i); ll temp=get_S(x/i); ret=(ret+temp*(las-i+1)+mode)%mode; } ret=(1+mode-ret)%mode; return S[x]=ret; } ll get_sum(ll x) { ll las=0,ret=0; for(int i=1;i<=x;i=las+1) { las=x/(x/i); ret+=(las-i+1)*(x/i)%mode; ret%=mode; } return ret*ret%mode; } ll solve(ll n) { ll las=0,ret=0; for(int i=1;i<=n;i=las+1) { las=n/(n/i); ret+=(get_S(las)-get_S(i-1)+mode)%mode*get_sum(n/i)%mode; ret%=mode; } return ret; } int main() { init(); ll n; scanf("%lld",&n); printf("%lld\n",solve(n)); return 0; }