UVA 11440 - Help Tomisu(数论好题)

题目链接 https://cn.vjudge.net/problem/UVA-11440

【题意】
输入整数 nm ,统计区间 [2,n!] 之间有多少个整数 x 满足 x 的所有素因子都大于 m (2<=n<=107,1<=m<=n,nm<=105) 答案对 100000007 取模

【思路】
首先 m<=n 所以 n!m! 的整数倍,而且 一个数的所有素因子都大于m 等价于 这个数和m!互素 ,而且根据gcd的性质,对于k>m!,km! 互素等价于 k modm!m! 互素. 这样一来,只要求出“不超过 m! 且和 m! 互素的数的个数” 再乘以 n!m! 即可,关键就在于求出 phi(m!)phifac(n)=phi(n!) 根据欧拉函数公式

phi(n)=n(11p1)(11p2)...(11pk)

如果 n 不是素数,那么 n!(n1)! 的素因子集合完全相同,
phifac(n)=phifac(n1)×n

如果 n 是素数,还会多一项 (11n)=n1n 约分得
phifac(n)=phifac(n1)×(n1)
还要特别注意m=1的情况

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

const int mod=100000007;
const int maxn=10000005;

int fac[maxn];
int invfac[maxn];
int phifac[maxn];
bool notprime[maxn];
int n,m;

void gcd(ll a,ll b,ll &d,ll& x,ll& y){
    if (0==b){ d=a;x=1;y=0; }
    else { gcd(b,a%b,d,y,x);y-=x*(a/b); }
}

ll inv(ll a,ll p){
    ll d,x,y;
    gcd(a,p,d,x,y);
    return d==1?(x+p)%p:-1;
}

void init(){
    notprime[0]=notprime[1]=true;
    for(int i=2;i<maxn;++i){
        if(!notprime[i]) for(int j=i*2;j<maxn;j+=i) notprime[j]=true;
    }
    fac[0]=fac[1]=1;
    for(int i=2;i<maxn;++i) fac[i]=(ll)fac[i-1]*(ll)i%mod;
    for(int i=0;i<maxn;++i) invfac[i]=inv(fac[i],mod);
    phifac[1]=phifac[2]=1;
    for(int i=3;i<maxn;++i){
        phifac[i]=(ll)phifac[i-1]*(ll)(notprime[i]?i:i-1)%mod;
    }
}

int main(){
    init();
    while(scanf("%d%d",&n,&m)==2){
        if(0==n && 0==m) break;
        int ans=phifac[m];
        ans=(ll)ans*(ll)fac[n]%mod;
        ans=(ll)ans*(ll)invfac[m]%mod;
        printf("%d\n",(ans-1+mod)%mod);
    }
    return 0;
}
posted @ 2018-08-28 21:07  不想吃WA的咸鱼  阅读(103)  评论(0编辑  收藏  举报