• [SDOI2015]约数个数和


    [SDOI2015]约数个数和

    题目

    解法

    由于(N,M)的顺序对答案没影响
    我们规定(N<M)
    (ans=sum_{i=1}^Nsum_{j=1}^M d(ij))

    给出一个结论

    [d(ij)=sum_{x|i}sum_{y|i}[gcd(x,y)=1] ]

    所以

    [ans=sum_{i=1}^Nsum_{j=1}^Msum_{x|i}sum_{y|i}[gcd(x,y)=1] ]

    乱搞一下,改成枚举因子

    [ans=sum_{i=1}^Nsum_{j=1}^Mfrac{N}{i}frac{M}{j}[gcd(i,j)=1] ]

    显然,来一发莫比乌斯反演

    [f(x)=sum_{i=1}^Nsum_{j=1}^Mfrac{N}{i}frac{M}{j}[gcd(i,j)=x] ]

    所以(ans=f(1))

    [g(x)=sum_{x|d}f(x) ]

    [g(x)=sum_{x|d}sum_{i=1}^Nsum_{j=1}^Mfrac{N}{i}frac{M}{j}[gcd(i,j)=d] ]

    [g(x)=sum_{i=1}^Nsum_{j=1}^Mfrac{N}{i}frac{M}{j}[gcd(i,j) \% x=0] ]

    (x)提出来我们就可以排除(gcd)的影响

    [g(x)=sum_{i=1}^{frac{N}{x}}sum_{j=1}^{frac{M}{x}}frac{N}{ix}frac{M}{jx}[gcd(i,j) \% 1=0] ]

    [g(x)=sum_{i=1}^{frac{N}{x}}sum_{j=1}^{frac{M}{x}}frac{N}{ix}frac{M}{jx} ]

    根据莫比乌斯反演定理

    [f(x)=sum_{x|d}μ(frac{d}{x})g(d) ]

    [f(x)=sum_{x|d}μ(frac{d}{x})sum_{i=1}^{frac{N}{d}}sum_{j=1}^{frac{M}{d}}frac{N}{id}frac{M}{jd} ]

    (ans=f(1))所以

    [ans=sum_{d=1}^Nμ(d)sum_{i=1}^{frac{N}{d}}sum_{j=1}^{frac{M}{d}}frac{N}{id}frac{M}{jd} ]

    [ans=sum_{d=1}^Nμ(d)sum_{i=1}^{frac{N}{d}}frac{N}{id}sum_{j=1}^{frac{M}{d}}frac{M}{jd} ]

    先用数论分块(O(nsqrt n))预处理一个$$sum(x)=sum_{i=1}^xfrac{x}{i}$$
    于是$$ans=sum_{d=1}^Nμ(d)sum(frac{N}{d})sum(frac{M}{d})$$
    显然我们可以把前面一部分预处理前缀和,后面数论分块加上前面的预处理可以(O(sqrt n )的算出每一次的答案)

    完整代码

    #include<bits/stdc++.h>
    #define ll long long
    using namespace std;
    const int N=1e6,MAX=50000;
    int prime[N],isprime[N],miu[N],cnt,sum[N];
    void pre(){
        miu[1]=1;
        for(int i=2;i<=MAX;++i){
            if(!isprime[i])miu[i]=-1,prime[++cnt]=i;
            for(int j=1;j<=cnt&&i*prime[j]<=MAX;++j){
                isprime[i*prime[j]]=1;
                if(i%prime[j]==0)break;
                miu[i*prime[j]]=-miu[i];
            }
        }
        for(int i=1;i<=MAX;++i)miu[i]+=miu[i-1];
        for(int i=1;i<=MAX;++i){
            
            for(int l=1,r;l<=i;l=r+1){
                r=i/(i/l);
                r=min(r,i);
                sum[i]+=(r-l+1)*(i/l);
            }
        }
    }
    int main(){
        int T=0;
        cin>>T;
        pre();
        while(T--){
            int n,m;
            ll ans=0;
            cin>>n>>m;
            if(n>m)swap(n,m);
            for(int l=1,r;l<=n;l=r+1){
                r=min(n/(n/l),m/(m/l));
                ans+=1ll*(miu[r]-miu[l-1])*sum[n/l]*sum[m/l];
            }
            printf("%lld
    ",ans);
        }
        return 0;
        
    }
    
    
  • 相关阅读:
    使用Oracle Wrap工具加密你的代码
    Oracle wrap 和 unwrap( 加密与解密) 说明
    oracle_base和oracle_home 的区别
    Oracle的SOME,ANY和ALL操作
    Oracle自主事务处理
    oracle读写文件--利用utl_file包对磁盘文件的读写操作
    Oracle中序列的使用
    INSTEAD OF触发器
    DBMS_LOB包的使用
    Oracle入门4-REF Cursor
  • 原文地址:https://www.cnblogs.com/ljq-despair/p/8683769.html
Copyright © 2020-2023  润新知