BZOJ2186 欧拉函数

时间:2021-07-15 02:53:40

欧拉函数:一般记作φ(n),表示1-n中与n互质的数的数量。

欧拉函数是积性函数,即φ(m*n)=φ(m)*φ(n)    //这条定理基友面试时还遇到了= =

欧拉函数的值φ(n)=n*(1-p[1])*(1-p[2])*...*(1-p[n])  //p[i]是小于等于n的所有素数

若n是m的倍数,则小于等于n且与m互质的数的个数为(n/m)*φ(m)    //证明不难理解:设k小于等于m且与m互质,则k+m、k+2m......也与m互质

若n是质数p的k次幂,则φ(n)=(p-1)*(p^(k-1))

//关于欧拉函数wikipedia上讲的很详细,此处不赘述了

若a mod b=0,可记作b|a

那么原题的答案就是(n!/m!)*φ(m!)

isprime(n):计算1-n的质数

calc_fac(n):计算1-n每个数的阶乘

calc_inv(n):计算1-n每个数的逆元

calc_eul(n):计算(1-1/p1)*(1-1/p2)*...*(1-1/pn)

       设x[i]=inv(p[i]),那么

       (1-1/p[i]) mod m

      = ( (1 mod m) - ( (1/p[i]) mod m ) ) mod m

      = (m+1-( (1/p) mod m ) mod m

          而根据逆元的性质,有(1/p) mod m=x mod m

Reference:http://blog.csdn.net/acdreamers/article/details/8220787

      http://www.cnblogs.com/autsky-jadek/p/4054971.html

附SDOI官方题解:

 该题即求1至n!中与m!互质的数对某质数R取模后的值 。
对于每一对数N,M,设p1,p2,……pn为不大于M的质数,题目即求在1至N!中,不包含因子p1,p2,……pn的数的个数。
在1至N!中,p1的倍数有N!/p1个,p2的倍数有n!/p2个……p1p2的倍数有n!/p1p1个……p1p2p3..pkm的倍数有n!/p1p2p3..pkm个。
由容斥定理得答案为:
n!-n!/p1-n!/p2-n!/p2…-n!/pkm+n!/p1p2+n!/p1p3……+(-)^km*n!/p1p2p3…pkm= n!*(-/p1)*(-/p2)*(-/p3)*(-/p4)…*(-/pkm)
记m!* (-/p1)*(-/p2)*(-/p3)*(-/p4)…*(-/pkm)为fi[m]。答案为n!/m!*fi[m]。
根据fi的计算式,可得fi的递推式为
Fi[i]=fi[i-]*i(当i为合数)
Fi[i]=fi[i-]*(i-)(当i为质数) 预处理得所有fi与i!,通过扩展GCD计算除法,可在O(logR)时间内计算得每一个解。

各种卡时限,cin、cout是用不了的,关了同步都不行>_<

 #include "cstring"
#include "math.h"
#include "stdio.h"
using namespace std;
#define MMX 10000002
#define LL long long
LL M,N,T,MOD;
LL fa[MMX],inv[MMX],ans2[MMX];
bool pr[MMX]; void isprime(LL n) //pr[i]=1 : i is a prime
{
memset(pr,true,sizeof(pr));
LL m=sqrt(n+0.5);
pr[]=false;
for (LL i=;i<=m;i++)
if (pr[i])
{
for (LL j=i*i;j<=n;j+=i)
pr[j]=false;
}
} void calc_fac(LL n) //fa[i]=i!
{
fa[]=;
for (LL i=;i<=n;i++)
fa[i]=fa[i-]*i%MOD;
} void calc_inv(LL n) //inv[i]
{
inv[] = ;
for(int i=;i<n;i++) //inv[i]:逆元
{
if(i >= MOD) break;
inv[i] = (MOD - MOD / i) * inv[MOD % i] % MOD;
}
} void calc_eul(LL n)
{
ans2[] = ; //ans2[i]=(1-1/p1)*(1-1/p2)*...*(1-1/pi)
for(LL i=; i<n;i++) //又(1-1/pi)=((pi-1)/pi)
{
if(pr[i])
{
ans2[i] = ans2[i-] * (i - ) % MOD;
ans2[i] = ans2[i] * inv[i % MOD] % MOD;
}
else
{
ans2[i] = ans2[i-];
}
}
} int main()
{
scanf("%d%d",&T,&MOD);
isprime(MMX);
calc_inv(MMX);
calc_fac(MMX);
calc_eul(MMX);
while (T--)
{
scanf("%d%d",&N,&M);
//ans=(N!/M!)*f(M!)
// =N!*[(1-1/p1)*...*(1-1/pi)]
LL ans=fa[N]*ans2[M]%MOD;
printf("%lld\n",ans);
} return ;
}

最后过得好险= =

771794 i386DX 2186 Accepted 244956 kb 11312 ms C++/Edit 1653 B 2014-11-04 23:34:15