Description
传送门:http://www.lydsy.com/JudgeOnline/problem.php?id=2186
大富翁国因为通货膨胀,以及假钞泛滥,政府决定推出一项新的政策:现有钞票编号范围为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
题解
本题其实不算一个难题,题意显然就是要求在N!内与M!互质的数的个数,我们先来看在M!内与M!互质的数,很显然就是一个欧拉函数,可以线性的求出,那么在N!内呢?显然如果i与M!是互质的,那么i+M!也与M!互质,i+2×M!也与M!互质,那么在N!内与M!互质的个数即为 ϕ(M!)×N!M!mod(r) .
又由于 ϕ(M!)=M!×p1−1p1×p2−2p2×... ,
所以又可以化为 N!×Π(pi−1)/pi(modr)
其中pi指的是M!的不同质因子,显然这个式子中, n!mod(r) 可以O(n)预处理,而 Π(pi−1)/pi 也是可以O(n)预处理的.
所以可以O(n)预处理,O(1)查询;
下面我们来看看
Π(pi−1)/pi
的求法,假设我们令a[m]表示
Π(pi−1)/pi(modr)
,pi为m!的质因子,那么我们可以分情况讨论,当m为质数时,那么m!的质因子与(m-1)!的质因子只有一个不同,那就是多了一个m,所以此时
a[m]=a[m−1]×m
.而如果m不为质数,那么m!的质因子与(m-1)!的质因子是完全一样的,不同的只是某个因子的指数,所以此时
a[m]=a[m−1]
,至此,
Π(pi−1)/pi
我们也在线性时间下算出了,问题也就解决了。
代码
/**************************************************************
Problem: 2186
User: zyg0121
Language: C++
Result: Accepted
Time:6408 ms
Memory:130196 kb
****************************************************************/
#include<cstdio>
#include<cmath>
#include<ctime>
#include<cstring>
#include<iostream>
#include<algorithm>
const int inf = 1000000000;
const int N = 10000000;
typedef long long ll ;
using namespace std;
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;
}
int T,R,n,m,cnt;
int fac[10000005],ine[10000005],pri[500005],ans[10000005];
bool mark[10000005];
void exgcd(int a,int b,int &x,int &y) {
if(b==0) {
x=1;
y=0;
return;
}
exgcd(b,a%b,x,y);
int t=x;
x=y;
y=t-a/b*y;
}
int getine(int t) {
int x,y;
exgcd(t,R,x,y);
return (x%R+R)%R;
}
void pre() {
fac[1]=1;
for(int i=2; i<=N; i++)fac[i]=(ll)fac[i-1]*i%R;
ine[1]=1;
for(int i=2; i<=N; i++) {
if(!mark[i])pri[++cnt]=i,ine[i]=getine(i);
for(int j=1; pri[j]*i<=N&&j<=cnt; j++) {
mark[pri[j]*i]=1;
if(i%pri[j]==0)break;
}
}
ans[1]=1;
for(int i=2; i<=N; i++) {
ans[i]=ans[i-1];
if(!mark[i])ans[i]=(ll)ans[i]*(i-1)%R*ine[i]%R;
}
}
int main() {
T=read();
R=read();
pre();
while(T--) {
n=read();
m=read();
printf("%d\n",(ll)fac[n]*ans[m]%R);
}
return 0;
}
下面有一份80分的在线代码,最后几个点RE,烦请大神帮帮蒟蒻吧!!!
#include <cstdio>
#include <iostream>
#include <cmath>
using namespace std;
const int size = 1000000+50;
typedef long long LL;
LL f[size],p[size],a[size];
int now = 1,top = 1,tot = 1;
int r,n,m;
int prime[500050];
bool v[size];
void isprime() {
LL ans=0;
for(int i=2; i<=size; i++) {
if(!v[i]) prime[++ans]=i;
for(int j=1; j<=ans&&i*prime[j]<=size; j++) {
v[i*prime[j]]=true;
if(i%prime[j]==0)
break;
}
}
return ;
}
void mod(int t) {
while(top<t)
f[++top]=f[top-1]*(top%r)%r;
}
LL inv (int t) {
if(t<=tot)
return p[t];
else
while(tot<t)
p[++tot]=(r-r/tot)*p[r%tot]%r;
return p[tot];
}
LL ans(int t) {
if(t<=now)
return a[t];
while(now<t) {
a[++now]=a[now-1];
if(!v[now])
a[now]=a[now]*(now-1)%r*inv(now%r)%r;
}
return a[t];
}
int main() {
int T;
scanf("%d%d",&T,&r) ;
isprime();
a[1]=1;p[0]=0;
p[1]=1;f[1]=1;
while(T--) {
scanf("%d%d",&n,&m);
if(n>top)
mod(n);
printf("%lld\n",f[n]*ans(m)%r);
}
return 0;
}