【莫比乌斯反演】BZOJ3309 DZY Loves Math

时间:2022-05-22 21:10:42

Description

  对于正整数n,定义f(n)为n所含质因子的最大幂指数。例如f(1960)=f(2^3 * 5^1 * 7^2)=3, f(10007)=1, f(1)=0。
  给定正整数a,b,求sigma(sigma(f(gcd(i,j)))) (i=1..a, j=1..b)。T<=1e4; a,b<=1e7。

Solution

  一开始没仔细看数据范围然后打了一个每个询问O(n)的,当然T了

  (盗一张图)

  【莫比乌斯反演】BZOJ3309 DZY Loves Math

  一开始我按照第二行的做的,里层外层循环都和ab有关,每一层都要sqrt(n)

  然后发现f(d)和ab无关,于是把f放到里面,把和ab有关的拎出来,就变成了第三行的式子

  这样里面一层循环与ab无关,可以预处理好

  我们要求的就是后面sigma的前缀和

  不难想到nlogn的预处理,但这题比较卡还是T

  于是要这么做

  

  设g(T)=Σ[d|T]f(d)μ(T/d)

  大力分析

  将T质因数分解,对于每一个p^a,T/d的p系数要么为0要么为1,否则μ(T/d)一定为0不考虑

  如果存在ai!=aj,关于T的因数p按a可以分为两个集合,a最大A集合和a非最大的B集合

  f取值由A集合的选取决定

  μ由选取的总个数决定

  无论A怎么选,在B中选取的奇偶方案数相同,于是总贡献一定为0

  也就是如果存在ai!=aj, g(T)=0

  那么a都相等的情况

  选奇数选偶数方案相同贡献也为0

  但如果p全部都选那么f的贡献为a-1(其余选法f贡献都为a)

  所以要多减一个1,考虑μ的影响,对于有k个p的T,g(T)=(-1)^(k+1)

  具体的计算方法在线性筛的时候记录一个当前最小素数的次数和去掉最小素数后上一个数

  如果清楚线性筛的原理那么还是很好想的

  预处理复杂度同线性筛,询问复杂度为sqrt(n)

Code

 #include<cstdio>
#include<algorithm>
#include<cstring>
#define ll long long
using namespace std;
const int maxn=1e7+; bool flag[maxn]; int prime[maxn],cnt;
int t[maxn],last[maxn],g[maxn];
int n,m; void getmu(){
for(int i=;i<=1e7;i++){
if(!flag[i]){
prime[++cnt]=i;
last[i]=t[i]=;
g[i]=;
}
for(int j=;i*prime[j]<=1e7&&j<=cnt;j++){
int x=i*prime[j];
flag[x]=;
if(i%prime[j]==){
last[x]=last[i];
t[x]=t[i]+;
if(last[x]==)
g[x]=;
else
g[x]=(t[last[x]]==t[x]?-g[last[x]]:);
break;
}
last[x]=i;
t[x]=;
g[x]=(t[i]==?-g[i]:);
}
}
for(int i=;i<=1e7;i++)
g[i]+=g[i-];
} ll f(int x,int y){
ll ret=;
for(int i=,pos=;i<=x;i=pos+){
pos=min(x/(x/i),y/(y/i));
ret+=1ll*(g[pos]-g[i-])*(x/i)*(y/i);
}
return ret;
} int main(){
getmu(); int T;
scanf("%d",&T);
while(T--){
scanf("%d%d",&n,&m);
if(n>m) swap(n,m);
printf("%lld\n",f(n,m));
}
return ;
}