原题链接: 洛谷:点我QωQ bzoj:点我QωQ (常数过不去。。。用另一种写法过的)
给定 n , m n,m n,m,保证 n > = m n>=m n>=m,求 [ 1 , n ! ] [1,n!] [1,n!]中与 m ! m! m!互质的有多少。
一行两个正整数 n , m ( n , m < = 1 e 7 ) n,m(n,m<=1e7) n,m(n,m<=1e7)。
如题意简述
我们会发现,对于一个数 p p p,用一个 01 01 01序列表示一个序列和 p p p互质的情况, 0 0 0表示不互质, 1 1 1表示互质。那么, [ 1 , p ] [1,p] [1,p]的这个 01 01 01序列和 [ p + 1 , 2 p ] [p+1,2p] [p+1,2p], [ 2 p + 1 , 3 p ] , ⋯ [ ( n − 1 ) p + 1 , n p ] [2p+1,3p],\cdots [(n-1)p+1,np] [2p+1,3p],⋯[(n−1)p+1,np]是一样的。
不信?我们试试。如 p = 9 p=9 p=9。那么, [ 1 , p ] [1,p] [1,p]的这个 01 01 01序列应该等于 1 1 0 1 1 0 1 1 0 1\ 1\ 0\ 1\ 1\ 0\ 1\ 1\ 0 1 1 0 1 1 0 1 1 0 会发现, [ p + 1 , 2 p ] [p+1,2p] [p+1,2p]的这个 01 01 01序列也等于: 1 1 0 1 1 0 1 1 0 1\ 1\ 0\ 1\ 1\ 0\ 1\ 1\ 0 1 1 0 1 1 0 1 1 0
所以,设 [ 1 , p ] [1,p] [1,p]中和 p p p互质的有 k k k个,那么 [ 1 , n p ] [1,np] [1,np]中和 p p p互质的就有 n k nk nk个。回到这个题中,我们只要求出 [ 1 , m ! ] [1,m!] [1,m!]中多少个和 m ! m! m!互质,然后用它 ∗ n ! m ! *\frac{n!}{m!} ∗m!n!即珂。但是,如何求 [ 1 , m ! ] [1,m!] [1,m!]中多少个和 m ! m! m!互质(即 ϕ ( m ! ) \phi(m!) ϕ(m!))呢?
我们知道一个式子: ϕ ( x ) = x ∗ ∏ i = 1 k p i − 1 p i \phi(x)=x*\prod\limits_{i=1}^{k}\frac{p_i-1}{p_i} ϕ(x)=x∗i=1∏kpipi−1,其中 p 1 , p 2 . . . p k p_1,p_2...p_k p1,p2...pk是 x x x的所有质因数。即 x x x的 ϕ \phi ϕ值为: x ∗ x 所 有 质 因 数 − 1 x 所 有 质 因 数 x*\frac{x所有质因数-1}{x所有质因数} x∗x所有质因数x所有质因数−1。而 m ! m! m!的所有质因数就是 1 m 1~m 1 m中的所有质数。也就是说,我们的答案就是 n ! m ! ∗ ∏ i = 1 k p k − 1 p k \frac{n!}{m!}*\prod\limits_{i=1}^{k}\frac{p_k-1}{p_k} m!n!∗i=1∏kpkpk−1。(其中 p k p_k pk是 m m m以内最大的质数)
所以,我们预处理好所有的质数,然后设 p r e [ i ] pre[i] pre[i]为前 i i i个质数的 p − 1 p \frac{p-1}{p} pp−1值之积(即前缀积)。每次询问的时候, u p p e r _ b o u n d upper\_bound upper_bound一下 m m m的位置,乘一下即珂。当然我们也要求出阶乘(也是前缀积的思想)。会发现,那个 p r e pre pre数组在算的时候要涉及到除法,两个阶乘也要除,那没问题,跑一下逆元就珂以了。
代码:
#include<bits/stdc++.h> using namespace std; namespace Flandle_Scarlet { #define N 10000005 #define int long long int t,r; int n,m; int primes[N];bool notp[N];//质数 int inv[N];//逆元 int fact[N];//阶乘 int pre[N];//前缀积 int qpow(int a,int b,int m)//快速幂 { int r=1; while(b) { if (b&1) r=r*a%m; a=a*a%m,b>>=1; } return r; } void Init()//O(n)=O(1e7) { int n=10000000; int& cnt=primes[0]; notp[1]=1; for(int i=2;i<=n;++i) { if (!notp[i]) { primes[++cnt]=i; } for(int j=1;j<=cnt and i*primes[j]<=n;++j) { int u=primes[j]; notp[i*u]=1; if (i%u==0) break; } }//处理质数 for(int i=1;i<=primes[0];++i) { inv[i]=qpow(primes[i],r-2,r); }//处理质数的逆元 fact[0]=1; for(int i=1;i<=n;++i) { fact[i]=fact[i-1]*i%r; }//处理阶乘 pre[0]=1; for(int i=1;i<=primes[0];++i) { int u=primes[i]; pre[i]=pre[i-1]*(u-1)%r*inv[i]%r; }//处理pre } void Solve() { int ans=fact[n]; int pos=upper_bound(primes+1,primes+primes[0]+1,m)-primes-1; //upper_bound之后-1是最后一个<=某个数的位置 //lower_bound是第一个<=某个数的位置,所以不能用lower_bound ans*=pre[pos];ans%=r; printf("%lld\n",ans); } void Main() { if (0) { freopen("","r",stdin); freopen("","w",stdout); } scanf("%lld%lld",&t,&r); Init(); while(t--) { scanf("%lld%lld",&n,&m); Solve(); } } #undef N //10000005 #undef int //long long }; main() { Flandle_Scarlet::Main(); return 0; }回到总题解界面
