Description
大富翁国因为通货膨胀,以及假钞泛滥,政府决定推出一项新的政策:现有钞票编号范围为1到N的阶乘,但是,政府只发行编号与M!互质的钞票。房地产第一大户沙拉公主决定预测一下大富翁国现在所有真钞票的数量。现在,请你帮助沙拉公主解决这个问题,由于可能张数非常大,你只需计算出对R取模后的答案即可。R是一个质数。
Input
第一行为两个整数T,R。R<=10^9+10,T<=10000,表示该组中测试数据数目,R为模后面T行,每行一对整数N,M,见题目描述 m<=n
Output
共T行,对于每一对N,M,输出1至N!中与M!素质的数的数量对R取模后的值
Sample Input
1 11
4 2
Sample Output
1
数据范围:
对于100%的数据,1 < = N , M < = 10000000
HINT
Source
数论
假如一个数x和另一个数
所以对于一组N,M,答案应为φ(M!)∗N!M!
再来看φ(M!),M!=1∗2∗3∗...∗M,这个家伙虽然很大,但要知道它也是可以标准分解的,即M!=pa11pa22pa33...,其中p1,p2,p3...都是素数,由于欧拉函数的公式就可以知道φ(M!)=M!∗(p1−1)/p1∗(p2−1)/p2∗...,而M!覆盖面这么广(2到M都被它覆盖到了!),我们就可以知道p1,p2,p3...其实就是所有小于等于M的质数!
于是至此最终的答案就为
又注意到内存这么大,显然就是要预处理了,有以下4项:
1、所有素数(线筛即可)。
2、所有数的逆元(不能只处理那些素数的逆元)。
这里可以采取基于a∗i+b=p的求法,也就是下面这个程序段:
void workinv()
{
inv[1]=1;
for (int i=2;i<=N;++i)
{
inv[i]=(long long)(p-p/i)*inv[p%i]%p;
if (inv[i]<0) inv[i]+=p;
}
}
自己好好写写,应该不难理解。
3、阶乘。
4、一个ans数组,ans[i]存储所有小于等于i的素数的
另外有一个小优化:对于逆元、阶乘和ans数组,当大于p(即题目中的R)的时候就都为0了(因为要取模),所以预处理可以只处理到min(N,p)。
由于取模时总是出现负数,一气之下决定牺牲一部分时间,强行加回来。
#include<cstdio>
#include<algorithm>
#define isnum(t) ((t>=48)and(t<=57))
typedef long long ll;
inline void in(int &x)
{
char t=getchar();int f=1;x=0;
while(!isnum(t)){if(t=='-')f=-1;t=getchar();}
while(isnum(t)){x=x*10+t-48;t=getchar();}
x*=f;
}
using namespace std;
const int N=10000000;
int n,m,t,p,top;
int prime[N+10],inv[N+10],fac[N+10],ans[N+10];
bool notprime[N];
void workprime()
{
for (int i=2;i<=N;++i)
{
if (!notprime[i]) prime[++top]=i;
for (int j=1;(j<=top)and(i*prime[j]<N);++j)
{
notprime[i*prime[j]]=1;
if (i%prime[j]==0) break;
}
}
}
void workinv()
{
inv[1]=1;
for (int i=2;i<=min(N,p);++i)
{
inv[i]=(long long)(p-p/i)*inv[p%i]%p;
if (inv[i]<0) inv[i]+=p;
}
}
void workfac()
{
fac[1]=1;
for (int i=2;i<=min(N,p);++i)
{
fac[i]=(long long)fac[i-1]*i%p;
if (fac[i]<0) fac[i]+=p;
}
}
void workans()
{
ans[1]=1;
for (int i=2;i<=min(N,p);++i)
if (!notprime[i])
{
ans[i]=(long long)ans[i-1]*(i-1)%p;
ans[i]=(long long)ans[i]*inv[i]%p;
if (ans[i]<0) ans[i]+=p;
}
else ans[i]=ans[i-1];
}
int main()
{
in(t),in(p);
workprime();
workinv();
workfac();
workans();
while(t--)
{
int n,m;in(n),in(m);
printf("%lld\n",(long long)fac[n]*ans[m]%p);
}
return 0;
}