zoukankan      html  css  js  c++  java
  • 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
    ",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; 
    }
  • 相关阅读:
    几道cf水题
    cf水题
    一道cf水题
    c++list用法
    c++map用法
    c++ vector常见用法
    c++string,常见用法总结
    复变函数考试后的反思
    [FZYZOJ 1204] 零和问题
    [FZYZOJ 1202] 金坷垃
  • 原文地址:https://www.cnblogs.com/AKCqhzdy/p/7920146.html
Copyright © 2011-2022 走看看