[bzoj4659\2694]Lcm_数论_莫比乌斯反演
Lcm bzoj-4659 bzoj-2694
题目大意:给出A,B,考虑所有满足l<=a<=A,l<=b<=B,且不存在n>1使得n^2同时整除a和b的有序数对(a,b),求其lcm(a,b)之和。答案模2^30。
注释:$1\le A,B\le 4\cdot 10^6$,$1\le cases \le 2000$。
想法:这题是一道挺好的题,它的好在于对于题目的转化。
这题目描述,没个做,我们将它转化一下
$\ \ \sum\limits_{i=1}^A\sum\limits_{j=1}^Blcm(i,j)\mu(gcd(i,j))^2$
这时,就变成了一道反演的题目了。
$=\sum\limits_{i=1}^A\sum\limits_{j=1}^B\sum\limits_{d|i,d|j}[gcd(i,j)=d]\frac{ij}{d}\mu(d)^2$
$=\sum\limits_{d=1}^A\sum\limits_{i=1}^{\lfloor\frac{A}{d}\rfloor}\sum\limits_{j=1}^{\lfloor\frac{B}{d}\rfloor}[gcd(i,j)==1]\cdot dij \cdot \mu(d)^2$
$=\sum\limits_{d=1}^{A}\mu(d)^2d\sum\limits_{i=1}^{\lfloor\frac{A}{d}\rfloor}i\sum\limits_{j=1}^{\lfloor\frac{B}{d}\rfloor}j\sum\limits_{e|i,e|j}\mu(e)$
$=\sum\limits_{d=1}^A\mu(d)^2d\sum\limits_{e=1}^{\lfloor\frac{A}{d}\rfloor}\mu(e)e^2\sum\limits_{i=1}^{\lfloor\frac{A}{de}\rfloor}i\sum\limits_{j=1}^{\lfloor\frac{B}{de}\rfloor}j$
$=\sum\limits_{D=1}^AD\sum\limits_{d|D}\mu(d)^2\mu(\frac{D}{d})\frac{D}{d}sum(\lfloor\frac{A}{D}\rfloor)sum(\lfloor\frac{B}{D}\rfloor)$
此时,我们设函数
$f(x)=\sum\limits_{d|x}\mu(d)^2\mu(\frac{x}{d})\frac{x}{d}$
$g(x)=\mu(x)^2$
$h(x)=x\cdot\mu(x)$
因为g函数为积性函数,h函数为积性函数,所以函数$g\cdot h$为积性函数,故f函数为积性函数,所以我们可以线性筛。
最后,附上丑陋的代码... ...
#include <cstdio> #include <cstring> #include <iostream> using namespace std; typedef long long ll; const ll N=4000000; ll pri[N/10],np[N+10]; ll f[N+10],s[N+10],sum[N+10],ans,msk; ll num,cases,n,m; inline char nc() { static char buf[100000],*p1,*p2; return p1==p2&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++; } inline int read() { int x=0;char c=nc(); while(!isdigit(c))c=nc(); while(isdigit(c))x=x*10+c-'0',c=nc(); return x; } int main() { ll p,last; f[1]=s[1]=sum[1]=1; msk=1,msk<<=30,msk--; for(int i=2;i<=N;i++) { if(!np[i]) pri[++num]=i,f[i]=1-i; s[i]=s[i-1]+f[i]*i,sum[i]=sum[i-1]+i; for(int j=1;j<=num&&i*pri[j]<=N;j++) { p=pri[j],np[i*p]=1; if(i%p==0) { if(i%(p*p)==0) f[i*p]=0; else f[i*p]=f[i/p]*(-p); break; } f[i*p]=f[i]*(1-p); } } cases=read(); while(cases--) { n=read(),m=read(),ans=0; if(n>m) swap(n,m); for(int i=1;i<=n;i=last+1) { last=min(n/(n/i),m/(m/i)); ans+=(s[last]-s[i-1])*sum[n/i]*sum[m/i]; } printf("%lld\n",ans&msk); } return 0; }
小结:反演好玩qwq...
| 欢迎来原网站坐坐! >原文链接<