【数学/扩展欧几里得/线性求逆元】[Sdoi2008]沙拉公主的困惑
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取模后的值
首先以为n>=m,所以m!是n!的因数,所以每次找到一个与m!互质的数,那么我们就可以得到n!/m!个数与m!互质。
所以答案就是phi(m!)*n!/m!。
(i为质数)
那么易得(i为质数)
然后线性求1-1000W的fac[i](i!%R)和inv[i](i的逆元)即可(素数用exgcd,其他的用到inv是个积性函数(i,j互质,f[i*j]=f[i]*f[j]))。
phi值可以不用线性筛。。O(sqrt(n))求单个即可,实际效率更高。
inv只需要求素数的就可以辣。。(求其他的亲测tle,可能超时。。)
还有一个ans数组记录n!后面那一坨的值,具体求法:是素数,那么就ans[i]=ans[i]*(i-1)*inv[i]。(记得mod),否则不动,不过不管是不是素数,在操作之前都要ans[i]=ans[i-1],三个数组的头号元素值都是1.
1 #include<cstdio> 2 #include<cstring> 3 #include<cmath> 4 #include<algorithm> 5 6 #define maxn 10000000 7 8 typedef long long ll; 9 10 using namespace std; 11 12 int prime[maxn+1],b=0,inv[maxn+1],mod,fac[maxn+1],x,y,ans[maxn+1]; 13 14 bool is_prime[maxn+1]; 15 16 void exgcd(int n,int m) 17 { 18 if(m==0){x=1,y=0;return;} 19 exgcd(m,n%m); 20 ll t=x; 21 x=y,y=t-n/m*y; 22 } 23 24 void work() 25 { 26 inv[1]=1,fac[1]=1; 27 for(int i=2;i<=maxn&&i<mod;i++)fac[i]=(ll)i*fac[i-1]%mod; 28 for(int i=2;i<=maxn;i++) 29 { 30 if(!is_prime[i])prime[++b]=i,exgcd(i,mod),inv[i]=(x%mod+mod)%mod; 31 int j=1,t=2*i; 32 while(j<=b&&t<=maxn) 33 { 34 is_prime[t]=1; 35 if(i%prime[j]==0)break; 36 // inv[t]=inv[i]*inv[prime[j]]%mod; 37 t=prime[++j]*i; 38 } 39 } 40 ans[1]=1; 41 for(int i=2;i<=maxn;i++) 42 { 43 ans[i]=ans[i-1]; 44 if(!is_prime[i])ans[i]=(ll)ans[i]*(i-1)%mod*inv[i]%mod; 45 } 46 } 47 48 int main() 49 { 50 int T; 51 scanf("%d%d",&T,&mod); 52 work(); 53 while(T--) 54 { 55 int x,y; 56 scanf("%d%d",&x,&y); 57 printf("%d\n",(ll)fac[x]*ans[y]%mod); 58 } 59 return 0; 60 }
long long什么的卡的我不要不要的。。