zoukankan      html  css  js  c++  java
  • 【bzoj4176】Lucas的数论 【莫比乌斯反演】【杜教筛】

    题目链接
    题意:i=1nj=1nf(ij)1000000007的值。f(n)为n的约数个数。
    题解:我们有一个结论:f(nm)=i|nj|m(gcd(i,j)==1)
    这是为什么呢?
    考虑到nm的任何一个约数都可以表示成满足i|nj|mimj的形式,上面的式子就是枚举i,j的值的过程。那为什么要求gcd(i,j)==1呢?如果有d满足d|id|j那么imj=ipmjp,重复计算了。所以我们有了上面的式子。
    ans
    =i=1nj=1nf(ij)
    =i=1nj=1na|ib|j(gcd(a,b)==1)
    =i=1nj=1na|ib|jk|gcd(a,b)μ(k)
    =i=1nj=1na|ib|jk|a,k|bμ(k)
    =k=1nμ(k)a=1nkb=1nki=1nakj=1nbk
    =k=1nμ(k)(i=1nknik)2
    于是我们用杜教筛算出μ的前缀和,再分块计算即可。
    杜教筛
    i=1nj|iμ(j)=1,因为只有i=1时有贡献1。
    =>j=1ni=1njμ(j)=1
    =>i=1nj=1niμ(j)=1
    =>j=1nμ(j)=1i=2nj=1niμ(j),即把i=1带入。
    于是我们可以通过记忆化搜索求出前缀和。如果预先线性筛出较小的前缀和,时间复杂度大概为O(n23)我也不会算
    代码:

    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    using namespace std;
    typedef long long ll;
    const int N=5000005,M=1000033;
    const ll mod=1000000007;
    int n,p[N/10];
    ll ans,tmp,mu[N];
    bool vis[N];
    struct HashSet{
        int cnt,head[M],to[M],nxt[M];
        ll v[M];
        bool count(int f){
            int x=f%M;
            for(int i=head[x];i;i=nxt[i]){
                if(to[i]==f){
                    return true;
                }
            }
            return false;
        }
        ll get(int f){
            int x=f%M;
            for(int i=head[x];i;i=nxt[i]){
                if(to[i]==f){
                    return v[i];
                }
            }
        }
        void set(int f,ll val){
            int x=f%M;
            to[++cnt]=f;
            nxt[cnt]=head[x];
            v[cnt]=val;
            head[x]=cnt;
        }
    }s;
    ll solve(int n){
        if(n<=5000000){
            return mu[n];
        }
        if(s.count(n)){
            return s.get(n);
        }
        ll res=1;
        for(int i=2,last;i<=n;i=last+1){
            last=n/(n/i);
            res-=solve(n/i)*(last-i+1);
            res%=mod;
        }
        s.set(n,res);
        return res;
    }
    ll calc(int n){
        ll res=0;
        for(int i=1,last;i<=n;i=last+1){
            last=n/(n/i);
            res+=(n/i)*(last-i+1)%mod;
            res%=mod;
        }
        return res;
    }
    int main(){
        mu[1]=1;
        for(int i=2;i<=5000000;i++){
            if(!vis[i]){
                p[++p[0]]=i;
                mu[i]=-1;
            }
            for(int j=1;j<=p[0]&&i*p[j]<=5000000;j++){
                vis[i*p[j]]=true;
                if(i%p[j]){
                    mu[i*p[j]]=-mu[i];
                }else{
                    break;
                }
            }
        }
        for(int i=1;i<=5000000;i++){
            mu[i]+=mu[i-1];
            mu[i]%=mod;
        }
        scanf("%d",&n);
        for(int i=1,last;i<=n;i=last+1){
            last=n/(n/i);
            tmp=calc(n/i);
            ans+=tmp*tmp%mod*(solve(last)-solve(i-1))%mod;
            ans%=mod;
        }
        printf("%lld
    ",(ans+mod)%mod);
        return 0;
    }
  • 相关阅读:
    Mysql一分钟定位 Next-Key Lock,你需要几分钟
    golang 服务大量 CLOSE_WAIT 故障排查
    Mysql大并发热点行更新的两个骚操作
    golang 服务诡异499、504网络故障排查
    golang 服务平滑重启小结
    vim 列编辑模式
    特邀全球互联网技术大会(麒麟会GITC)分享 —大型团购系统架构设计
    ElasticSearch 评分排序
    zookeeper 实现分布式锁安全用法
    诡异的druid链接池链接断开故障经验总结
  • 原文地址:https://www.cnblogs.com/2016gdgzoi471/p/9476865.html
Copyright © 2011-2022 走看看