题目的意思大概是求1~N!中和M!互质的数的个数
因为对欧拉函数理解不够深刻所以我是分析得到结果的:
当N<=M的时候显然符合要求的数的个数为0;
当N>M的时候我们要求的是1~N!中不含1 ~M的素因子的的数的个数,结合欧拉函数的推导过程(容斥原理)假设N!在1 ~ M中含有k个素因子,设n=N!,那么符合要求的数的个数就是
n−n/p1−n/p2−...−n/pk+n/p1p2+n/p1p3+...+n/p(k−1)pk−...=n(1−1/p1)(1−1/p2)...(1−1/pk)n-n/p1-n/p2-...-n/pk+n/p1p2+n/p1p3+...+n/p(k-1)pk-...=n(1-1/p1)(1-1/p2)...(1-1/pk)n−n/p1−n/p2−...−n/pk+n/p1p2+n/p1p3+...+n/p(k−1)pk−...=n(1−1/p1)(1−1/p2)...(1−1/pk)
因为我们不能首先求出n的大小再算(如果可以求出n的大小的话那显然直接计算就可以,但是我们必须要模R),在模R的情况下我们应该求出pi的逆元,所以整个过程就是线性筛得到1~M的素数因子以及他们的逆元,然后与N!相乘计算(按照上面的公式)
我的代码:
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<iostream>
#include<cmath>
#include<ctime>
#include<climits>
#include<queue>
#include<vector>
#include<set>
#include<map>
using namespace std;typedef long long ll;
const int INF=0x3f3f3f3f;
const int MAXN=1e7+5;
int prime[MAXN];
bool check[MAXN];
int N[MAXN],r[MAXN],ans[MAXN];
int tot;
ll R,n,m;void ex_gcd(int a,int b,int& d,int &x,int &y)
{if(!b) {d=a; x=1; y=0;}else{ex_gcd(b,a%b,d,y,x); y-=(a/b)*x;}
}int getine(int a)
{int x,y,d;ex_gcd(a,R,d,x,y); x=(x%R+R)%R;return x;
}void creat_prime()
{tot=0; r[1]=1; N[1]=1;for(int i=2;i<MAXN;i++){N[i]=(ll)N[i-1]*i%R;if(!check[i]){prime[tot++]=i;r[i]=getine(i);}for(int j=0;j<tot && prime[j]*i<MAXN;j++){check[prime[j]*i]=true;if(i%prime[j]==0) break;}}
}void init()
{ans[1]=1;for(int i=2;i<MAXN;i++){ans[i]=ans[i-1];if(!check[i]) ans[i]=(ll)ans[i]*(i-1)%R*r[i]%R;}
}int main()
{int T;scanf("%d%d",&T,&R);creat_prime();init();while(T--){scanf("%d%d",&n,&m);printf("%d\n",(ll)N[n]*ans[m]%R);}return 0;
}
虽然可以得到正确的答案,但是经过了自己的推导分析,思维质量比较低。根本原因还是对欧拉函数的理解不够深刻。
让我们再来看看欧拉函数的意义:求出1~n中和n互质的数的个数,那么1 ~kn中与n互质的数的个数有多少呢?答案是k*phi[n](结合上面推导也可以发现这个)。为什么是这样呢?
互质即意味着gcd(x,n)=1gcd(x,n)=1gcd(x,n)=1,由最大公约数的性质我们得到gcd(x+kn,n)=1gcd(x+kn,n)=1gcd(x+kn,n)=1即这个数在每次kn+1~(k+1)n中都会出现一次,所以答案是k*phi[n]
回到这个问题:题目要求1~N!中和M!互质的数的个数,那么答案应该是N!/M!*phi[M!],再结合欧拉函数值的求法可以得到上面的过程。
学习了一下递推法求逆元.(递推法求逆元如果不是都要用时间还是慢一点)
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<iostream>
#include<cmath>
#include<ctime>
#include<climits>
#include<queue>
#include<vector>
#include<set>
#include<map>
using namespace std;typedef long long ll;
const int INF=0x3f3f3f3f;
const int MAXN=1e7+5;
int prime[MAXN];
bool check[MAXN];
int N[MAXN],r[MAXN],ans[MAXN];
int tot;
ll R,n,m;int read()
{int x=0;char ch=getchar();while(ch<'0'||ch>'9')ch=getchar();while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}return x;
}void ex_gcd(int a,int b,int& d,int &x,int &y)
{if(!b) {d=a; x=1; y=0;}else{ex_gcd(b,a%b,d,y,x); y-=(a/b)*x;}
}int getine(int a)
{int x,y,d;ex_gcd(a,R,d,x,y); x=(x%R+R)%R;return x;
}void creat_prime()
{tot=0; r[1]=1; N[1]=1;for(int i=2;i<MAXN;i++){N[i]=(ll)N[i-1]*i%R;if(!check[i]){prime[tot++]=i;//r[i]=getine(i);}for(int j=0;j<tot && prime[j]*i<MAXN;j++){check[prime[j]*i]=true;if(i%prime[j]==0) break;}}for(int i=2;i<MAXN&&i<R;i++)r[i]=R-(ll)R/i*r[R%i]%R;
}void init()
{ans[1]=1;for(int i=2;i<MAXN;i++){ans[i]=ans[i-1];if(!check[i]) ans[i]=(ll)ans[i]*(i-1)%R*r[i]%R;}
}int main()
{int T;T=read(); R=read();creat_prime();init();while(T--){n=read(); m=read();printf("%d\n",(ll)N[n]*ans[m]%R);}return 0;
}