BZOJ 2693: jzptab 莫比乌斯反演 + 积性函数 + 筛法
Description
Input
题解:
这次是多组询问,上次推出来的式子是 $O(n)$ 的,我们需要更快的算法.
先定义几个后面可能会用到的函数:(在本题弱化版中推出来的)
$Sum(n,m)=\frac{n(n+1)}{2}\times \frac{m(m+1)}{2}$
$calc(n,m)=\sum_{d=1}^{n}\mu(d)d^2Sum(\frac{n}{d},\frac{m}{d})$
则 $ans=\sum_{d=1}^{n}d\times calc(\frac{n}{d},\frac{m}{d})$
把式子展开:
$=\sum_{d=1}^{n}d\sum_{b=1}^{\frac{n}{d}}\mu(b)b^2Sum(\frac{n}{db},\frac{m}{db})$
令 $D=db$ ,得:
$\sum_{D=1}^{n}Sum(\frac{n}{D},\frac{m}{D})\sum_{d|D}d\times\mu(\frac{D}{d})\times(\frac{D}{d})^2$
看上去有点别扭,再稍微改动一下:
$\sum_{D=1}^{n}Sum(\frac{n}{D},\frac{m}{D})\sum_{d|D}\frac{D}{d}\times\mu(d)\times d^2$
前面 $\sum_{D=1}^{n}Sum(\frac{n}{D},\frac{m}{D})$ 这个部分好像没法优化了,只能$\sqrt{n}$ 硬算.
好在 $f(D)=\sum_{d|D}\frac{D}{d}\times\mu(d)\times d^2$ 是一个积性函数,可以线筛.
只需分三种情况讨论即可:
(1) $x$ 为质数.$\Rightarrow$ $f(x)=x^{2}-x$ (只有 $d=1,d=D$ 时有贡献)
(2) $a,b$ 互质.$\Rightarrow$ $f(ab)=f(a)f(b)$ (积性函数定义)
(3) $p|i$ ,且 $p$ 为质数, $i$ 中 $p$ 的指数为 $1$
$f(i)=\sum_{d|i}\frac{i}{d}\times\mu(d)\times d^2$
$f(i\times p)=\sum_{d|i\times p}\frac{i\times p}{d}\times\mu(d)\times d^2$
由 $\mu$ 的定义可知,当一个数有质因子次幂大于 $1$ 时值为 $0$ ,即无贡献.
那么,$f(i\times p)$ ,$d$ 中 $p$ 的幂次必小于等于 $1$.
而 $f(i)$ 中所有 $d$ 中 $p$ 的次幂本来就小于等于 $1$.
故 $f(i)$ 与 $f(i\times p)$ 中有贡献的因子是完全相同的.
唯一不同就是 $f(i\times p)=f(i)\times p$
既然讨论好上面 $3$ 种情况,我们就可以愉快地线筛 $f(x)$ 啦!!
#include<bits/stdc++.h> #define ll long long #define M 10001000 #define maxn 10200100 #define MOD 100000009 using namespace std; int cnt, tot; int vis[maxn],mu[maxn], prime[maxn]; ll h[maxn], sumv[maxn]; void init() { int i,j; h[1]=1; for(i=2;i<M;++i) { if(!vis[i]) prime[++tot]=i, h[i]=(i-(ll)i*i)%MOD; for(j=1;j<=tot&&prime[j]*i<M;++j) { vis[prime[j]*i]=1; if(i%prime[j]==0) { h[prime[j]*i]=(prime[j]*h[i])%MOD; break; } else h[prime[j]*i]=(h[prime[j]]*h[i])%MOD; } } for(i=1;i<M;++i) sumv[i]=(sumv[i-1]+h[i])%MOD; } ll SUM(ll x,ll y) { x%=MOD, y%=MOD; ll r1=(x*(x+1)>>1)%MOD; ll r2=(y*(y+1)>>1)%MOD; return (r1*r2)%MOD; } ll query(ll n,ll m) { int i,last,re=0,j; if(n>m) swap(n,m); for(i=1;i<=n;i=j+1) { j=min(n/(n/i), m/(m/i)); re += SUM(n/i, m/i) * (sumv[j]-sumv[i-1])%MOD; re%=MOD; } return (re+MOD)%MOD; } int main() { init(); int n,m,T; scanf("%d",&T); while(T--) { scanf("%d%d",&n,&m); printf("%lld\n",query(n,m)); } return 0; }