[cqoi2015]选数 解题报告

来源:互联网 发布:linux 卸载u盘 编辑:程序博客网 时间:2024/04/30 10:08

本题的性质在于,如果选了两个数,那么这两个数的最大公约数至大为H-L。(所以需要特判只选了一个数的情况)
先来看一下傻逼的做法:
考虑莫比乌斯反演,i*K对答案的贡献是μ(i)NHiKL1iK,对于所有数相同的情况,我们记一下每个数被多算了几次,最后减去即可。
膜拜一下大神的做法:
莫比乌斯反演考虑的是一个前缀和,但是我们为什么要前缀和呢?我们完全可以直接算以i*K为最大公约数的情况有多少啊,f(i)表示以i*K为最大公约数的情况数,那么答案就是f(i)=NHiKL1iKHLiKj=1f(ij)
莫比乌斯反演:

#include<cstdio>#include<iostream>using namespace std;int prime[30000],mu[100005];bool p[100005];#define Mod 1000000007typedef long long LL;LL pow(int a,int x){    LL ans=1,prod=a;    for(;x;x>>=1,prod=prod*prod%Mod)        if(x&1)            ans=ans*prod%Mod;    return ans;}#include<cmath>int cnt[100005];int main(){    freopen("bzoj_3930.in","r",stdin);    int i,j;    mu[1]=1;    for(i=2;i<=100000;++i){        if(!p[i]){            prime[++prime[0]]=i;            mu[i]=-1;        }        for(j=1;j<=prime[0]&&i*prime[j]<=100000;++j){            p[i*prime[j]]=1;            if(i%prime[j])mu[i*prime[j]]=-mu[i];            else break;        }    }    int N,K,L,H;    scanf("%d%d%d%d",&N,&K,&L,&H);    L=(L-1)/K,H=H/K;    int ans=0,x;    for(i=H-L;--i>0;)        if(mu[i]){            for(j=i*(L/i+1),x=0;j<=H;++x,j+=i)cnt[j-L]+=mu[i];            ans=(ans+mu[i]*pow(x,N))%Mod;        }    for(i=H-L;i>1;--i)        if(cnt[i])            ans=(ans-cnt[i])%Mod;    if(L==0)ans=(ans-(1-cnt[1]))%Mod;    else ans=(ans-cnt[1])%Mod;    cout<<(ans+Mod)%Mod<<endl;}

dp:

#include<cstdio>#include<iostream>using namespace std;#define Mod 1000000007typedef long long LL;int pow(int a,int x){    LL ans=1,prod=a;    for(;x;x>>=1,prod=prod*prod%Mod)        if(x&1)            ans=ans*prod%Mod;    return ans;}int f[100005];int main(){    freopen("cqoi15_number.in","r",stdin);    freopen("cqoi15_number.out","w",stdout);    int N,K,L,H,i,j;    scanf("%d%d%d%d",&N,&K,&L,&H);    L=(L-1)/K,H=H/K;    for(i=H-L-1;i;--i){        //printf("---%d----\n",i);        f[i]=(pow(H/i-L/i,N)-(H/i-L/i))%Mod;        //cout<<pow(H/i-L/i,N)<<"-"<<(H/i-L/i)<<"=";        for(j=i<<1;j<H-L;j+=i)f[i]=(f[i]-f[j])%Mod;        //cout<<f[i]<<endl;    }    printf("%d\n",((f[1]+(L==0))%Mod+Mod)%Mod);}

总结:
再统计类题目中务必要注意待求函数本身与其前缀和的相互转化,往往有时候有一个会更好求一些。

0 0
原创粉丝点击