zoukankan      html  css  js  c++  java
  • 【bzoj3930】[CQOI2015]选数 【莫比乌斯反演】【杜教筛】

    题目传送门
    题意:求从区间[L,H]LH为整数)中选取N个整数,使它们的gcdK的方案总数模1000000007的值。
    题解:我们令l=L1Kr=HK。则原问题等价于求从区间[l,r]中选取N个整数,使它们的gcd1的方案总数模1000000007的值。
    我们令F(i)为选数的gcd为i的倍数的方案总数,则显然F(i)=(rili)N
    我们令f(i)为选数的gcd为i的方案总数,则显然F(i)=i|df(d)
    莫比乌斯反演可得f(i)=i|dμ(di)F(d)
    把i=1带入,得f(1)=d=1rμ(d)(rdld)N
    于是这个式子我们可以先杜教筛求出μ的前缀和,再分块计算。
    杜教筛是什么?
    杜教筛点这里
    代码:

    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    using namespace std;
    typedef long long ll;
    const int N=1000005,M=1000033;
    const ll mod=1000000007;
    int n,k,l,r,p[N];
    ll ans,mu[N];
    bool vis[N];
    ll fastpow(ll a,ll x){
        a%=mod;
        ll res=1;
        while(x){
            if(x&1){
                res=res*a%mod;
            }
            x>>=1;
            a=a*a%mod;
        }
        return res;
    }
    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<=1000000){
            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)%mod;
            res%=mod;
        }
        s.set(n,res);
        return res;
    }
    int main(){
        scanf("%d%d%d%d",&n,&k,&l,&r);
        l=(l-1)/k;
        r/=k;
        if(!r){
            puts("0");
            return 0;
        }
        mu[1]=1;
        for(int i=2;i<=1000000;i++){
            if(!vis[i]){
                p[++p[0]]=i;
                mu[i]=-1;
            }
            for(int j=1;j<=p[0]&&i*p[j]<=1000000;j++){
                vis[i*p[j]]=true;
                if(i%p[j]){
                    mu[i*p[j]]=-mu[i];
                }else{
                    break;
                }
            }
        }
        for(int i=2;i<=1000000;i++){
            mu[i]+=mu[i-1];
            mu[i]%=mod;
        }
        for(int i=1,last;i<=r;i=last+1){
            if(l/i){
                last=min(r/(r/i),l/(l/i));
            }else{
                last=r/(r/i);
            }
            ans+=fastpow(r/i-l/i,n)*(solve(last)-solve(i-1))%mod;
            ans%=mod;
        }
        printf("%lld
    ",(ans+mod)%mod);
        return 0;
    }
  • 相关阅读:
    贪心算法部分题目及知识点总结
    贪心算法(农夫修泥塘)
    贪心算法部分知识点
    丑数运算 一、((输出丑数n的下标)(给定丑数输下标)) 二、((求第n个丑数是谁)(给定下标求丑数))
    关于学习STL部分学到的零碎知识点
    STL中set与map的使用以及优先队列的部分补充内容以及重载运算符的使用
    回文素数与接水问题(OJ)
    关于字符串与整数转化的问题与一些常用字符串处理函数
    部分STL简单应用知识点
    【Python小游戏】俄罗斯方块
  • 原文地址:https://www.cnblogs.com/2016gdgzoi471/p/9476864.html
Copyright © 2011-2022 走看看