bzoj2820: YY的GCD
设F(i)=sigema(1~n) gcd(x,y)==i 的个数
ans=sigema(1~n,i为质数)i F(i);
设G(i)=sigema(1~n) gcd(x,y)==i 的倍数 的个数
那G(i)=(n/i)*(m/i),反演。以上做法是和2818一样的,但是由于多组数据就会T
观察一下反演后的式子:
F(i)=sigema(i|d)d u(d/i)*(n/d)*(m/d)
ans=sigema(i为质数)i sigema(i|d)d u(d/i)*(n/d)*(m/d)
但是实际上程序操作时转换成求F(1)等于说是
ans=sigema(i为质数)i sigema(1~n/i)d u(d)*(n/i/d)*(m/i/d)
设k=i*d
ans=sigema(1~n)k (n/k)*(m/k)*sigema(i|k,i为质数)i u(k/i)
设S(k)=sigema(i|k,i为质数)i u(k/i)
那可以离线做,就相当于k要枚举所有质因数。
假如暴力枚举k和他的质因数就是O(n*n/logn)照样挂,还有一种暴力的方法就是枚举sqrt(k),然后判素数,预处理一下素数,这个空间可能大点,然后时间就是O(n*sprt(n))
然后优化的方法就是枚举每个质数去更新每一个它影响的数,时间复杂度是O(logn*n/logn) (捂脸不知道怎么证质数更新是logn
应该够了。
然后我没写,有个更nb的方法就是像u一样在线性筛搞出来。
先回顾一下,u函数和这个数的值没有关系,只和这个数所包含的质因数,有相同质因数就为0,否则值和不同质因数个数奇偶性有关。
在线性筛里假如k被更新,那它肯定是被一个质数p乘一个数d被更新,分情况讨论一下,假如这个质数不被包含在d,也就是该点莫比乌斯函数值为1或-1的情况下,那k包含奇数个不同质数u值就等于-1,否则等于1,那k为质数,S(k)是等于1的( 只会加上u(1) ),通过这个延伸,假如k为两个不同质数乘积,那就是加上了两个质数的u值,就是-2了,由此延伸,S(k)加上的是k所包含的质数-1个的u值,那么S和u的正负性应该是相反的,而且类似的有S(k)=-S(k/任意一个质因数)+u(这个质因数),因为k/任意一个质因数 所包含的质因数是k的-1
否则假如这个质数被包含在d,就会导致 k/除p以外其他所有质因数 的u值都是0,因为他们包含了两个p,那么要加上的只有u(k/p)
PS:S函数和以往用u函数时一样,取前缀和分块加速。
#include<cstdio> #include<iostream> #include<cstring> #include<algorithm> using namespace std; typedef long long LL; LL prime[10001000],pr; LL u[10001000],S[10001000]; bool v[10001000]; void mobius_inversion() { pr=0;u[1]=1; memset(v,true,sizeof(v)); for(int i=2;i<=10000000;i++) { if(v[i]==true) { prime[++pr]=i; u[i]=-1;S[i]=1; } for(int j=1;j<=pr&&i*prime[j]<=10000000;j++) { v[i*prime[j]]=false; if(i%prime[j]==0){u[i*prime[j]]=0;S[i*prime[j]]=u[i];break;} u[i*prime[j]]=-u[i]; S[i*prime[j]]=-S[i]+u[i]; } S[i]+=S[i-1]; } } void solve(LL n,LL m) { LL ans=0,last; for(int k=1;k<=n;k=last+1) { last=min(n/(n/k),m/(m/k)); ans+=(S[last]-S[k-1])*LL(n/k)*LL(m/k); } printf("%lld\n",ans); } int main() { mobius_inversion(); int T; scanf("%d",&T); while(T--) { LL n,m; scanf("%lld%lld",&n,&m); if(n>m)swap(n,m); solve(n,m); } return 0; }