【BZOJ2186】沙拉公主的困惑(数论)
题面
题解
考虑答案是啥
先假设(n=m)
现在求的就是(varphi(m!))
但是现在(n!)是(m!)的若干倍
我们知道
(gcd(x,y)=gcd(x+ky,y))
所以,相当于
每隔(m!),答案增长的值都是(varphi(m!))
所以
我们可以得出
[ans=frac{n!}{m!}varphi(m!)
]
后面的(varphi)可以直接拆开,枚举质因数
[ans=frac{n!}{m!}·m!frac{prod (p_i-1)}{prod p_i}
]
[ans=n!frac{prod (p_i-1)}{prod p_i}
]
其中,(pi<=m)
所以,预处理(n!),质数的前缀乘,还有质数(-1)的前缀乘
至于逆元就不要预处理了(慢的死)
要用的时候直接快速幂算一下
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<set>
#include<map>
#include<vector>
#include<queue>
using namespace std;
#define MAX 10000000
inline int read()
{
int x=0,t=1;char ch=getchar();
while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
if(ch=='-')t=-1,ch=getchar();
while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
return x*t;
}
int R;
int n,m;
bool zs[MAX+1];
int pri[MAX/10],tot,jc[MAX+1];
int inv[MAX+1],pinv[MAX/10],ppri[MAX/10];
int Pos[MAX+1];
int fpow(int a,int b)
{
int s=1;
while(b){if(b&1)s=1ll*s*a%R;a=1ll*a*a%R;b>>=1;}
return s;
}
void pre()
{
zs[1]=true;inv[1]=jc[1]=1;
for(int i=2;i<=MAX;++i)
{
jc[i]=1ll*jc[i-1]*i%R;
if(!zs[i])pri[++tot]=i;//,inv[i]=fpow(i,R-2);
for(int j=1;j<=tot&&i*pri[j]<=MAX;++j)
{
zs[i*pri[j]]=true;
//inv[i*pri[j]]=1ll*inv[i]*inv[pri[j]]%R;
if(i%pri[j]==0)break;
}
}
pinv[0]=ppri[0]=1;
for(int i=1;i<=tot;++i)pinv[i]=1ll*pinv[i-1]*pri[i]%R;
for(int i=1;i<=tot;++i)ppri[i]=1ll*ppri[i-1]*(pri[i]-1)%R;
for(int i=1;i<=tot;++i)
for(int j=pri[i];j<pri[i+1];++j)
Pos[j]=i;
}
int Query(int n,int m)
{
return 1ll*jc[n]*fpow(pinv[Pos[m]],R-2)%R*ppri[Pos[m]]%R;
}
int main()
{
int T=read();R=read();
pre();
while(T--)
{
n=read();m=read();
printf("%d
",Query(n,m));
}
return 0;
}