[UVa 11440]Help Tomisu 数论 欧拉函数+拓欧逆元

本文解决了一个关于整数因子的数学难题,通过使用质数和欧拉函数来计算在特定范围内,所有质因数大于某个阈值的整数数量。该文提供了解题思路、关键算法实现以及实例解析,旨在帮助读者理解并掌握此类问题的求解方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Problem D
Help Mr. Tomisu 
Input: 
Standard Input

Output: Standard Output

 

After wasting a significant time of his life in problem-setting, Mr. Tomisu is now searching for glory: A glory that will make him famous likeGoldbach and rich like Bill Gates :). And he has chosen the field of Number Theory as his prime interest. His creator did not make him very bright and so he needs your help to solve an elementary problem, using which he will begin his pursuit for glory!

 

Tomisu has come to know that finding out numbers having large prime factors are very important in cryptography. Given two integers N and M,he aims to count the number of integers x between 2 and N! (factorial N), having the property that all prime factors of x are greater than M.

 

Input

The input file contains at most 500 lines of inputs. Each line contains two integers N (1<N<10000001) and M (1≤M≤N and N-M≤100000). Input is terminated by a line containing two zeroes. This line should not be processed.

 

Output

For each line of input produce one line of output. This line contains the value T % 100000007 (Modulo 100000007 value of T). Here T is the total number of numbers between 1 and N! (factorial N) which have prime factors greater than M. 

 

100 10

100 20

10000 9000

0 0

43274465

70342844

39714141


Problemsetter: Shahriar Manzoor

Special Thanks: Per Austrin

 


题目大意

求在1~N!之中,其所有质因数都大于M的数字个数。


解题思路

刚开始想到用容斥原理,处理数字中有偶数个不符合要求的质因数个数的,减去奇数个不符合要求质因数个数的。

对于每一个质因数都处理一下

TLE……

最后发现

 N (1<N<10000001) and M (1≤M≤N and N-M≤100000). 

500组数据,之前线性的处理方法也会直接挂

处理样例的数据发现答案是

(100!*48/210-1)mod 100000007

210 和 48 间的关系就是欧拉函数了……

换种思路,就是不超过M的所有质数的乘积P,所有数字种与P互质的个数,便是欧拉函数的典型应用了。(若不互质就拥有的小于等于M的质因数)

于是我们可以处理前k个质数的乘积,算出他们的欧拉函数(每个质数减一之积)。

就可以直接愉快的使用逆元了。

PS:由于UVa不限内存……可以直接把一些数据放在内存里咯,运行更快一些。

TLE的容斥代码:

#include <cstdio>
#include <iostream>
#include <algorithm>
#include <ctime>
#include <cctype>
#include <cmath>
#include <string>
#include <cstring>
#include <stack>
#include <queue>
#include <list>
#include <vector>
#include <map>
#include <set>

#define sqr(x) ((x)*(x))
#define LL long long 
#define INF 0x3f3f3f3f
#define PI acos(-1.0)
#define eps 1e-10
#define mod 100000007ll
using namespace std;
int co[100005];
LL pri[100005],pn;
void egcd(LL a,LL b,LL &x,LL &y)
{
    if (b==0)
    {
        x=1,y=0;
        return ;
    }
    egcd(b,a%b,x,y);
    int t=x;
    x=y,y=t-a/b*y;
}
int main()
{
    LL n,m;
    pn=0;
    for (int i=2;i<=100000;i++)
    {
        if (co[i]==0) {
        pri[++pn]=i;
        for (int j=2*i;j<=100000;j+=i)
            co[j]=1;
        }
    }
    while (~scanf("%lld%lld",&n,&m),n+m)
    {
        LL t=1;
        for (LL i=1;i<=n;i++)
            t=t*i%mod;
        LL preodd=0;
        LL preeven=t;
        LL ansodd,anseven;
        for (LL i=1;pri[i]<=m;i++)
        {
            LL x,y,di;
            egcd(pri[i],mod,x,y);
            // printf("%lld %lld \n", pri[i],i);
            di=(x+mod)%mod;
            ansodd=(preeven*di)%mod;//枚举之前的奇偶
            anseven=(preodd*di)%mod;
            ansodd=(ansodd+preodd)%mod;
            anseven=(anseven+preeven)%mod;
            // printf("%lld \n", ansodd);
            // printf("%lld \n", anseven);
            preeven=anseven;//保存下来
            preodd=ansodd;
            
        }
        LL ans=(-ansodd+anseven+mod-1)%mod;
        printf("%lld\n",ans);
    }
    return 0;
}


 AC代码:

#include <cstdio>
#include <iostream>
#include <algorithm>
#include <ctime>
#include <cctype>
#include <cmath>
#include <string>
#include <cstring>
#include <stack>
#include <queue>
#include <list>
#include <vector>
#include <map>
#include <set>

#define sqr(x) ((x)*(x))
#define LL long long 
#define INF 0x3f3f3f3f
#define PI acos(-1.0)
#define eps 1e-10
#define mod 100000007ll
using namespace std;
int co[10000115];
LL pri[10000115],pn;
LL fac[10000115];
LL ff[1000115];
LL fm[1000115];
void egcd(LL a,LL b,LL &x,LL &y)
{
    if (b==0)
    {
        x=1,y=0;
        return ;
    }
    egcd(b,a%b,x,y);
    int t=x;
    x=y,y=t-a/b*y;
}
int main()
{
    LL n,m;
    pn=0;
    for (int i=2;i<=10000107;i++)
    {
        if (co[i]==0) 
            pri[++pn]=i;
        for (int j=1;(i*pri[j]<=10000107)&&(j<=pn);j++)
        {
            co[i*pri[j]]=1;
            if (i%pri[j]==0) break;
        }
    }
    fac[1]=1;
    for (LL i=2;i<=10000107;i++)
        fac[i]=(fac[i-1]*i)%mod;
    ff[0]=1;
    fm[0]=1;
    for (int i=1;i<=pn;i++){
        fm[i]=(fm[i-1]*pri[i])%mod;
        ff[i]=(ff[i-1]*(pri[i]-1))%mod;}//fm[i]表示前i个质数的乘积,ff[i]表示对应的欧拉函数
    while (~scanf("%lld%lld",&n,&m),n+m)
    {
        LL t=fac[n];
        LL pt=upper_bound(pri+1,pri+pn+1,m)-pri;
        if (pri[pt]>=m) pt--;
        t=t*ff[pt]%mod;
        LL x,y;
        egcd(fm[pt],mod,x,y);
        x=(x+mod)%mod;
        t=(t*x-1+mod)%mod;
        printf("%lld\n",t);
    }
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值