P2155 [SDOI2008] 沙拉公主的困惑
关于题目
题目描述
大富翁国因为通货膨胀,以及假钞泛滥,政府决定推出一项新的政策:现有钞票编号范围为 1 到 N 的阶乘,但是,政府只发行编号与 M! 互质的钞票。房地产第一大户沙拉公主决定预测一下大富翁国现在所有真钞票的数量。现在,请你帮助沙拉公主解决这个问题,由于数量可能非常大,你只需计算出答案对 R 取模后的结果即可。
输入格式
第一行为两个整数 T 和 R,其中 T 为该组中测试数据数目,R 为模数。
接下来 T 行,每行一对整数 N 和 M,具体意义见题目描述。
输出格式
共 T 行,对于每一对 N 和 M,输出\([1,N!]\) 中与 M! 互质的数的数量对 R 取模后的值。
输入输出样例
1 11
4 2
1
一个特殊样例
1 3
4 3
2
题解
题目信息
\(M\le N\) 所以 \(M!\,|\, N!\)。
公式推导
设\(s=\gcd(a,b),x\times s=a,y\times s=b\),且\(\gcd(x,y)=1\),所以
设\(m | (x+y\times k)\),且\(m | y\),所以\(m|(y\times k)\),即\(m|x,m|y\),又因为\(\gcd(x,y)=1\),所以就有\(\gcd(x+y\times k,y)=m=1\),所以\(\gcd(a+b\times k)=s\times \gcd(x+y\times k,y)=s=\gcd(a,b)\)。
令\(b=M!\),所以就有任意\(k\in[1,N!/M!]\),即任意\(x\in[1,M!]\)时,有\(\gcd(k\times M!+x,M!)=\gcd(x,M!)\)。
也就是说任意\(k\in[1,N!/M!]\),\([(k-1)\times M!+1,k\times M!]\)中与\(M!\)中互质的数相等。
所以答案就是\(ans=N!/M!\times\Phi(M!)\)。
所以只需要去预处理出全部的逆元和阶乘,在进行O1的查询即可。
代码时刻
#include<bits/stdc++.h>
using namespace std;
const int maxn=10000000+10;
int t,r,n,m;
int phi[maxn],pri[maxn],fct[maxn];
bool vis[maxn];
inline long long qpow(int x,int y)
{
long long res=1;
while(y)
{
if(y&1)res=1ll*res*x%r;
x=1ll*x*x%r;
y>>=1;
}
return res;
}
inline void prepare1()
{
int cnt=0;
vis[0]=vis[1]=1;
for(int i=2;i<maxn;i++)
{
if(!vis[i])pri[++cnt]=i;
for(int j=1;j<=cnt;j++)
{
if(1ll*i*pri[j]>=maxn)break;
vis[i*pri[j]]=1;
if(!i%pri[j])break;
}
}
}
inline void prepare2()
{
fct[0]=phi[0]=1;
for(int i=1;i<maxn;i++)
{
int x=i,cnt=0,y=i-1+vis[i],cut=0;
while(x%r==0)
{
x/=r;
cnt++;
}
fct[i]=1ll*fct[i-1]*x%r;
while(y%r==0)
{
y/=r;
cut++;
}
phi[i]=1ll*phi[i-1]*y%r;
}
}
void print(long long x){
if(x<0){
putchar('-');
x=-x;
}
if(x>9)
{
print(x/10);
}
putchar(x%10+'0');
}
int main()
{
ios::sync_with_stdio(false);
prepare1();
cin >>t>>r;
prepare2();
while(t--)
{
cin >>n>>m;
if(n/r>m/r)
{
cout <<"0"<<endl;
continue;
}
long long ans=1ll*fct[n]*phi[m]%r*qpow(fct[m],r-2)%r;
print(ans);
puts("");
}
return 0;
}