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

 

posted @ 2017-11-29 13:58  AKCqhzdy  阅读(163)  评论(0编辑  收藏  举报