[CQOI2015]选数(莫比乌斯反演,杜教筛)

[CQOI2015]选数(luogu)

Description

题目描述

我们知道,从区间 [L,H](L 和 H 为整数)中选取 N 个整数,总共有 (H-L+1)^N 种方案。

小 z 很好奇这样选出的数的最大公约数的规律,他决定对每种方案选出的 N 个整数都求一次最大公约数,以便进一步研究。

然而他很快发现工作量太大了,于是向你寻求帮助。你的任务很简单,小 z 会告诉你一个整数 K,

你需要回答他最大公约数刚好为 K 的选取方案有多少个。

由于方案数较大,你只需要输出其除以 10^9+7 的余数即可。

输入格式

输入一行,包含四个空格分开的正整数,依次为 N,K,L,H。

输出格式

输出一个整数,为所求方案数。

数据范围

1 ≤ N,K ≤ 10^9,1 ≤ L ≤ H ≤ 10^9,H−L ≤ 10^5。

 

Soluiton

  • 先将 L、H 分别化为比 L、H 小的第一个 K 的倍数 / K 
  • 问题变成从 ( L , H ] 中选 N 个可重复的数,使它们的 gcd 为 1 的方案数 
  • 设 f(i)为从 ( L , H ] 中选 N 个可重复的数,使它们的 gcd 为 i 的方案数,
  • 发现 $F(i)=\sum\limits_{ i | d}^{} f(d)=(\lfloor H \rfloor - \lfloor L \rfloor)^N$,好求!

反演一下

$$f(i)=\sum\limits_{i|d,d<=H}^{}\mu(d)*F(d/i)$$

 

  • 然后整除分块,注意同时考虑 L,H
  • d可能很大,线性筛死了,杜教筛还活着

 

Code

 

#include <cstdio>
#include <cstdlib>
#include <map>
#include <algorithm>
#define ll long long
using namespace std;
const ll P=1e9+7;
const int N=6e6,M=6e6+10;
int    L,H,k,n,prime[M],tot,vis[M];
ll an,u[M];
map <int,ll> w;
ll qpow(int x,int y)
{
    ll ans=1,z=x;
    while(y)
    {
        if(y&1) ans=ans*z%P;
        z=z*z%P,y>>=1;
    }
    return ans;
}
ll F(int x)
{
    return qpow((H/x-L/x),n);
}
ll get(int x)
{
    if(x<=N) return u[x];
    if(w[x]) return w[x];
    int ans=1;
    for(int l=2,r;l<=x;l=r+1)
    {
        r=x/(x/l);
        ans-=(r-l+1)*get(x/l),ans%=P;
    }
    return w[x]=ans;
}
void init()
{
    u[1]=1;
    for(int i=2;i<=N;i++)
    {
        if(!vis[i]) prime[++tot]=i,u[i]=-1;
        for(int j=1;j<=tot && prime[j]<=N/i;j++)
        {
            vis[prime[j]*i]=1;
            if(i%prime[j]==0) break;
            u[prime[j]*i]=-u[i];
        }
        u[i]+=u[i-1];
    }
}
int main()
{
    init();
    scanf("%d%d%d%d",&n,&k,&L,&H);
    L=(L-1)/k,H=H/k;
    for(int l=1,r;l<=H;l=r+1)
    {
        if(!(L/l)) r=H/(H/l);
        else r=min(H/(H/l),L/(L/l));
        an=(an+(get(r)-get(l-1))%P*F(l)%P)%P;
    }
    printf("%lld\n",(an+P)%P);
    return 0;
}

 

 

 

posted @ 2020-02-27 15:48  hsez_cyx  阅读(127)  评论(0)    收藏  举报