BZOJ 2693: jzptab 莫比乌斯反演 + 积性函数 +筛法

时间:2024-11-10 23:37:38

Description

BZOJ 2693: jzptab 莫比乌斯反演 + 积性函数 +筛法

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;
}