组合数取模深度解析

来源:互联网 发布:手机怎么注册淘宝店铺 编辑:程序博客网 时间:2024/04/30 05:32
 

组合数取模深度解析

分类: 数论 63人阅读 评论(0) 收藏 举报

今天我们来讨论如何求:C(n,m)%p

首先如果n,m,p的范围均为[1,1000],那么我们完全可以用两个循环搞定,C(n,m)=(C(n-1,m)%p+C(n-1,m-1)%p)%p。

 

但是如果n,m,p的范围均为[1,10^9],另外p为素数,则完全可以用Lucas定理搞定。

 

如果n,m,p范围还是[1,10^9],另外p如果为合数,则我们可以先把p分解:p=p1^a1*p2^a2*...*pk^ak。

问题主要转化为如何求C(n,m)%(p^c)  其中p为素数。求完直接合并一个模方程即可。p^c 的规模大约是10^5。c 不是1,lucas阻止不了它。

n,m太大,因子分解也阻止不了它。

 

下面介绍一种好方法:

假设 p = 3, c = 2,也就是mod 9,假设n = 19,n! = 1 * 2 * 3 * 4 * 5 * 6 * 7 * 8 * …… * 19

要是可以快速得到 n! 中除掉3 以后 mod 9的结果,那么多好呀! 看3多讨厌,直接砍fac( int n) :

n! = [ 1 * 2 * 4 * 5 * 7 * 8 * …  * 16 * 17 * 19 ] * (3 * 6 * 9 * 12 * 15 * 18)=[ 1 * 2 * 4 * 5 * 7 * 8 * …  * 16 * 17* 19 ] * 3^6*( 1 * 2 * 3 * 4 * 5 * 6)

然后发现后面的一坨实际上是 fac( n / p) ,再看前半部分,尼玛是以 p^c 为周期的啊!!!

[1 * 2 * 4 * 5 * 7 * 8 ] = [10 * 11 * 13 * 14 * 16 *17 ] = (mod 9),于是说白了,对于前面的部分,由于周期,都是浮云了,下面是 孤立出来的19

可以知道孤立出来的 长度不超过 p^c ,于是暴力啊,暴力啊!于是完美解决n! 中和 p无关的项 mod p^c的值!!!

 

接下来是分母部分,一模一样,无非多了一个求逆元(因为都和p没关系了,逆元必然存在)

我们来分析一下,这样的复杂度是如何的呢,每次递归,规模变为原来的 1/p,logp N的啊!!!

当然是层数= =于是问题完美解决

[cpp] view plaincopy
  1. #include <iostream>  
  2. #define LL long long  
  3.   
  4. LL p,k,r,x,y;  
  5.   
  6. void extend_Euclid(LL a,LL b)  
  7. {  
  8.     if(b==0)  
  9.     {  
  10.         x=1;  
  11.         y=0;  
  12.         return;  
  13.     }  
  14.     extend_Euclid(b,a%b);  
  15.     LL temp=x;  
  16.     x=y;  
  17.     y=temp-(a/b)*y;  
  18. }  
  19.   
  20. LL quick_mod(LL a,LL b,LL m)  
  21. {  
  22.     LL ans=1;  
  23.     a%=m;  
  24.     while(b)  
  25.     {  
  26.   
  27.         if(b&1)  
  28.         {  
  29.             ans=ans*a%m;  
  30.             b--;  
  31.         }  
  32.         b>>=1;  
  33.         a=a*a%m;  
  34.     }  
  35.     return ans;  
  36. }  
  37.   
  38. LL multi(LL p,LL k)  
  39. {  
  40.     LL i,ret=1;  
  41.     for(i=1;i<=k;i++)  
  42.        ret*=p;  
  43.     return ret;  
  44. }  
  45.   
  46. LL sum(LL n)  
  47. {  
  48.     LL ans=0;  
  49.     while(n)  
  50.     {  
  51.         ans+=n/p;  
  52.         n/=p;  
  53.     }  
  54.     return ans;  
  55. }  
  56.   
  57. LL Solve(LL n)  
  58. {  
  59.     LL i;  
  60.     if(n==0) return 1;  
  61.     LL ans1=1;  
  62.     for(i=1;i<=r;i++)  
  63.     {  
  64.         if(i%p!=0)  
  65.            ans1=ans1*i%r;  
  66.     }  
  67.     LL ret=quick_mod(ans1,n/r,r);  
  68.     LL x=n-n%r+1;  
  69.     LL ans2=1;  
  70.     for(i=x;i<=n;i++)  
  71.     {  
  72.         if(i%p!=0)  
  73.            ans2=ans2*i%r;  
  74.     }  
  75.     ret=ret*ans2%r*Solve(n/p)%r;  
  76.     return ret;  
  77. }  
  78.   
  79. int main()  
  80. {  
  81.     LL n,m;  
  82.     LL ans,ret;  
  83.     LL x1,x2,x3,x4,x5;  
  84.     while(std::cin>>n>>m>>p>>k)  
  85.     {  
  86.         r=multi(p,k);  
  87.         x1=sum(n);  
  88.         x2=sum(n-m);  
  89.         x3=sum(m);  
  90.         x4=x1-x2-x3;  
  91.         ans=multi(p,x4);  
  92.         x1=Solve(n);  
  93.         x2=Solve(n-m);  
  94.         x3=Solve(m);  
  95.         x4=x2*x3%r;  
  96.         extend_Euclid(x4,r);  
  97.         x=(x%r+r)%r;  
  98.         x5=x1*x%r;  
  99.         ret=ans*x5%r;  
  100.         std::cout<<ret<<std::endl;  
  101.     }  
  102.     return 0;  
  103. }  


Petr在CodeForces上给世界级选手出的10道难题2 :

输出C(n,k)的最后10位,0<=k<=n<=10^18,n>=1


 

HDU3802深度解析

分类: 数论 66人阅读 评论(0) 收藏 举报

题目:Ipad,IPhone AC大神的题目

 

对于这个题我们首先得知道二次剩余的一个结论,就是如果p为奇素数,a为p的二次剩余,那么有a^((p-1)/2)=1(mod p),

如果a为p的二次非剩余,则a^((p-1)/2)=-1(mod p),如果为非剩余则答案直接为0,否则前面那两坨为4,只需考虑后面的,后面的用矩阵构造。

在降幂的时候注意必须是a和b都为p的二次剩余时才可以,否则就错误。还有在p已经为素数的情况下,对于a^bmod p 那么当b大于p-1时 

 a^(b%(p-1))%p与a^(b%(p-1)+p-1)%p是一样的结果。


 

[cpp] view plaincopy
  1. #include <iostream>  
  2. #define N 2  
  3.   
  4. typedef struct  
  5. {  
  6.     long long m[N][N];  
  7. }Matrix;  
  8.   
  9. Matrix I={1,0,0,1};  
  10.   
  11. Matrix multi(Matrix a,Matrix b,long long p)  
  12. {  
  13.     Matrix c;  
  14.     int i,j,k;  
  15.     for(i=0;i<N;i++)  
  16.     {  
  17.         for(j=0;j<N;j++)  
  18.         {  
  19.             c.m[i][j]=0;  
  20.             for(k=0;k<N;k++)  
  21.             {  
  22.                 c.m[i][j]+=a.m[i][k]*b.m[k][j]%p;  
  23.             }  
  24.             c.m[i][j]%=p;  
  25.         }  
  26.     }  
  27.     return c;  
  28. }  
  29.   
  30. Matrix multi_mod(Matrix a,long long k,long long m)  
  31. {  
  32.     Matrix ans=I,p=a;  
  33.     while(k)  
  34.     {  
  35.         if(k&1)  
  36.         {  
  37.             ans=multi(ans,p,m);  
  38.             k--;  
  39.         }  
  40.         k>>=1;  
  41.         p=multi(p,p,m);  
  42.     }  
  43.     return ans;  
  44. }  
  45.   
  46. long long quick_mod(long long a,long long b,long long m)  
  47. {  
  48.     long long ans=1;  
  49.     a%=m;  
  50.     while(b)  
  51.     {  
  52.         if(b&1)  
  53.         {  
  54.             ans=ans*a%m;  
  55.             b--;  
  56.         }  
  57.         b>>=1;  
  58.         a=a*a%m;  
  59.     }  
  60.     return ans;  
  61. }  
  62.   
  63. int main()  
  64. {  
  65.     int t;  
  66.     Matrix res;  
  67.     long long a,b,n,p;  
  68.     long long x,y,z,ret;  
  69.     std::cin>>t;  
  70.     while(t--)  
  71.     {  
  72.         Matrix A={0,1,1,1};  
  73.         std::cin>>a>>b>>n>>p;  
  74.         x=quick_mod(a,(p-1)>>1,p);  
  75.         y=quick_mod(b,(p-1)>>1,p);  
  76.         if(x!=1||y!=1)  
  77.         {  
  78.             std::cout<<"0"<<std::endl;  
  79.             continue;  
  80.         }  
  81.         Matrix ans=multi_mod(A,n,p-1);  
  82.         z=ans.m[0][0]+ans.m[0][1];  
  83.   
  84.         z%=(p-1);  
  85.         res.m[0][0]=2*(a+b)%p;  
  86.         res.m[0][1]=-(a-b)*(a-b)%p;  
  87.         res.m[1][0]=1;  
  88.         res.m[1][1]=0;  
  89.         ans=multi_mod(res,z-1,p);  
  90.         ret=(ans.m[0][0]*((2*a+2*b)%p)%p+ans.m[0][1]*2%p)%p;  
  91.         ret=(ret+p)%p;  
  92.         ret=(ret*4)%p;  
  93.         std::cout<<ret<<std::endl;  
  94.     }  
  95.     return 0;  

原创粉丝点击