天天看點

[CQOI2015]選數(bzoj3930 莫比烏斯反演+杜教篩+累加有上下界)

題目:

[CQOI2015]選數

題意:

[CQOI2015]選數(bzoj3930 莫比烏斯反演+杜教篩+累加有上下界)

思路:

若L%k==0,那麼累加的下界為

[CQOI2015]選數(bzoj3930 莫比烏斯反演+杜教篩+累加有上下界)

,若L%k!=0,那麼累加的下界為 

[CQOI2015]選數(bzoj3930 莫比烏斯反演+杜教篩+累加有上下界)

,綜合一下,下界為

[CQOI2015]選數(bzoj3930 莫比烏斯反演+杜教篩+累加有上下界)

我們設下界 = t 。

原式 =

[CQOI2015]選數(bzoj3930 莫比烏斯反演+杜教篩+累加有上下界)

[CQOI2015]選數(bzoj3930 莫比烏斯反演+杜教篩+累加有上下界)

[CQOI2015]選數(bzoj3930 莫比烏斯反演+杜教篩+累加有上下界)

[CQOI2015]選數(bzoj3930 莫比烏斯反演+杜教篩+累加有上下界)

(莫比烏斯反演)

[CQOI2015]選數(bzoj3930 莫比烏斯反演+杜教篩+累加有上下界)
[CQOI2015]選數(bzoj3930 莫比烏斯反演+杜教篩+累加有上下界)

把a除掉後,上屆為

[CQOI2015]選數(bzoj3930 莫比烏斯反演+杜教篩+累加有上下界)

,下界為

[CQOI2015]選數(bzoj3930 莫比烏斯反演+杜教篩+累加有上下界)

=

[CQOI2015]選數(bzoj3930 莫比烏斯反演+杜教篩+累加有上下界)

是以,

[CQOI2015]選數(bzoj3930 莫比烏斯反演+杜教篩+累加有上下界)

是以,可以對 g(d) 進行數論分塊,通過杜教篩求莫比烏斯函數的字首和。

code:

#include<bits/stdc++.h>
using namespace std;

typedef long long ll;

const int MAX = 1e6;
const ll MOD = 1000000007;

ll n,k,L,H;
ll mu[MAX+10],prime[MAX+10];
bool check[MAX+10];

inline ll read()
{
    register ll x=0,t=1;register char ch=getchar();
    while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
    if(ch=='-')t=-1,ch=getchar();
    while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
    return x*t;
}

void get_sum()
{
    memset(check,false,sizeof(check));
    mu[1]=1;
    int tot=0;
    for(int i=2;i<=MAX;i++){
        if(!check[i]){
            prime[tot++]=i;
            mu[i]=-1;
        }
        for(int j=0;j<tot&&i*prime[j]<=MAX;j++){
            check[i*prime[j]]=true;
            if(i%prime[j]==0){
                mu[i*prime[j]]=0;
                break;
            }
            else{
                mu[i*prime[j]]=mu[i]*(-1);
            }
        }
    }
    for(int i=2;i<=MAX;i++)
        mu[i]=mu[i-1]+mu[i];
}

map<ll,ll>dp;

ll cal(ll x)
{
    if(x<=MAX)   return mu[x];
    if(dp[x])   return dp[x];
    ll pos;
    ll tmp=0;
    for(ll i=2;i<=x;i=pos+1){
        pos=x/(x/i);
        tmp+=(pos-i+1)*cal(x/i);
    }
    dp[x]=1-tmp;
    return dp[x];
}

ll multiply(ll a, ll b)
{
	ll ans = 1;
	while (b)
	{
		if (b & 1)
		{
			ans = ((ans%MOD)*(a%MOD)) % MOD;
			b--;
		}
		b /= 2;
		a = ((a%MOD)*(a%MOD)) % MOD;
	}
	return ans;
}

int main()
{
    get_sum();
    n=read();
    k=read();
    L=read();
    H=read();
    ll ans=0;
    ll x=H/k;
    ll y=(L-1)/k;
    ll pos=0;
    ll i;
    for(i=1;i<=x;i=pos+1){
        if(i<=y)
            pos=min(x/(x/i),y/(y/i));
        else
            pos=x/(x/i);
        ans=(ans+multiply(x/i-y/i,n)*((cal(pos)-cal(i-1)+MOD)%MOD)%MOD)%MOD;
    }
    printf("%lld\n",ans);
    return 0;
}