UVA 11440 - Help Tomisu(数论好题)

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

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

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

p h i ( n ) = n ( 1 1 p 1 ) ( 1 1 p 2 ) . . . ( 1 1 p k )

如果 n 不是素数,那么 n ! ( n 1 ) ! 的素因子集合完全相同,
p h i f a c ( n ) = p h i f a c ( n 1 ) × n

如果 n 是素数,还会多一项 ( 1 1 n ) = n 1 n 约分得
p h i f a c ( n ) = p h i f a c ( n 1 ) × ( n 1 )
还要特别注意 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;
}

猜你喜欢

转载自blog.csdn.net/xiao_k666/article/details/82154557