BZOJ 3930 选数 (莫比乌斯反演+神奇的杜教筛)

来源:互联网 发布:淘宝童装拍照技巧 编辑:程序博客网 时间:2024/05/16 18:45

(对于100%的数据,1≤N,K≤10^9,1≤L≤H≤10^9,H-L≤10^5)


这个题要是数据范围出小一些解释个裸的莫比乌斯反演。

问题就是数据范围太鬼畜,我们无法筛出这个范围的莫比乌斯函数前缀和。

这时就要用到杜教筛,它是用来解决 n<= 10^11 时的一些积性函数的前缀和问题的

本鶸对此理解还不深,大家可以自行上网找相关博客。

这一篇就很好

author: skywalkert
original article: skywalkert
last update time : 2017-04-01


然后就是裸的莫比乌斯过程了


代码:

#include <iostream>#include <stdio.h>#include <math.h>#include <string.h>#include <vector>#include <algorithm>#include <map>using namespace std;typedef long long LL;#define fi first#define se secondconst int MAXN=10000000+23;const LL MOD=1e9+7;map<LL,LL>mu_sum;LL miu[MAXN],prime[MAXN],total;bool isprime[MAXN];void make(){    LL m=MAXN-3;    memset(isprime,true,sizeof(isprime));    isprime[1]=isprime[0]=false;    miu[1]=1;    total=0;    for(int i=2;i<=m;i++)    {        if(isprime[i])prime[++total]=i,miu[i]=-1;        for(int j=1;j<=total&&prime[j]*i<=m;j++)        {            isprime[i*prime[j]]=false;            if(i%prime[j])miu[i*prime[j]]=-miu[i];            else {miu[i*prime[j]]=0;break;}        }    }    for(int i=2;i<=m;i++)miu[i]+=miu[i-1];}LL cal(int x){    if(x<=10000000)        return miu[x];    if(mu_sum.find(x)!=mu_sum.end())        return mu_sum[x];    long long i,last,re=1;    for(i=1;i<=x;i=last+1)    {        last=x/(x/i);        if(x/i-1)            re-=(cal(last)-cal(i-1))*(x/i-1);    }    return mu_sum[x]=re;}LL quick(LL x,LL n){    LL res=1;    while(n)    {        if(n&1)res=res*x%MOD;        x=x*x%MOD;        n>>=1;    }    return res%MOD;}LL n,k,h,l;int main(){    make();    while(scanf("%lld%lld%lld%lld",&n,&k,&l,&h)!=-1)    {        l--;l/=k;h/=k;        LL res=0;        for(LL i=1,last;i<=h;i=last+1)        {            if(i<=l)last=min(l/(l/i),h/(h/i));            else last=h/(h/i);            LL a=h/i,b=l/i;            res+=(cal(last)-cal(i-1))*quick((a-b),n);            res=(res%MOD+MOD)%MOD;        }        printf("%lld\n",res%MOD);    }    return 0;}

一开始自己写的巨丑,看了网上dalao的之后才写对

另外还有一种递推的写法,那个我真心看不懂



阅读全文
0 0
原创粉丝点击