[CQOI2015]选数

Solution

可以先把 \(k\) 的限制去了,区间变为 \([\lceil\frac{L}{k}\rceil,\lfloor \frac{H}{k} \rfloor]\),记为 \([l,r]\) 。那么只需要使 gcd 为一。

考虑一波反演,定义 \(F(n)\) 表示 gcd 是 \(n\) 的倍数的方案数,\(f(n)\) 表示 gcd 恰好是 \(n\) 的方案数,那么

\[F(n)=\Big( \lfloor \frac{r}{n} \rfloor - \lfloor \frac{l-1}{n} \rfloor \Big )^N=\sum_{n|d} f(d) \]

则有

\[f(n)=\sum_{d|n} \mu(\frac{d}{n})F(d) \]

答案就是

\[f(1)=\sum_{i=1}^{r} \mu(i)\Big( \lfloor \frac{r}{i} \rfloor - \lfloor \frac{l-1}{i} \rfloor \Big )^N \]

套一个整数分块,然后用杜教筛求 \(mu\) 的区间和就可以了。注意整数分块的时候,\(F\) 的值和 \(\frac{r}{i}\)\(\frac{l-1}{i}\) 都有关。

#include<stdio.h>
#include<unordered_map>
#include<cassert>
using namespace std;
#define ll long long

const int N=5e6+7;
const int Mod=1e9+7;

bool mk[N];
int p[N],cnt=0;
ll L,R,mu[N];
unordered_map<int,int> mp;

ll qpow(ll x,ll y){
    ll ret=1;
    while(y){
        if(y&1) ret=ret*x%Mod;
        x=x*x%Mod,y>>=1;
    }
    return ret;
}

void pre(){
    mu[1]=1;
    for(int i=2;i<N;i++){
        if(!mk[i]){p[++cnt]=i,mu[i]=-1;}
        for(int j=1;j<=cnt&&p[j]*i<N;j++){
            mk[p[j]*i]=1;
            if(i%p[j]==0) break;
            mu[i*p[j]]=-mu[i];
        }
        mu[i]=(mu[i-1]+mu[i]+Mod)%Mod;
    }
}

ll solve(ll x){
    if(x<N) return mu[x];
    if(mp[x]) return mp[x];
    ll ret=1;
    for(ll l=2,r=0;l<=x;l=r+1){
        r=x/(x/l);
        ret=(ret-(r-l+1)*solve(x/l)%Mod+Mod)%Mod;
    }
    return mp[x]=ret;
}

int main(){
    ll n,k,H; pre();
    scanf("%lld%lld%lld%lld",&n,&k,&L,&H);
    L=(L-1)/k+1,R=H/k;
    ll ans=0;
    for(ll l=1,r=0;l<=R;l=r+1){
        if(l>=L) r=R/(R/l);
        else r=min(R/(R/l),(L-1)/((L-1)/l));
        ans=(ans+qpow(R/l-(L-1)/l,n)*(solve(r)-solve(l-1)+Mod)%Mod)%Mod;
    }
    printf("%lld",ans);
}
posted @ 2021-06-24 22:14  Kreap  阅读(39)  评论(0编辑  收藏  举报