HDU 5446 Unknown Treasure(lucas + 中国剩余定理 + 模拟乘法)

题目链接: http://acm.hdu.edu.cn/showproblem.php?pid=5446

题目大意:求C(n, m) % M, 其中M为不同素数的乘积,即M=p1*p2*...*pk, 1≤k≤10。1mn10^18。

分析: 如果M是素数,则可以直接用lucas定理来做,但是M不是素数,而是素数的连乘积。令C(n, m)为 X ,则可以利用lucas定理分别计算出 X%p1,X%p2, ... , X % pk的值,然后用中国剩余定理来组合得到所求结果。

比较坑的地方是,中间结果会超long long 范围,要用类似于快速幂的方法来模拟。

参考代码:

 1 #include <iostream>
 2 #include <stdio.h>
 3 #include <string.h>
 4 using namespace std;
 5 long long a[110001];
 6 long long Mod;
 7 long long c[110001];
 8 long long w[1000];
 9 long long Egcd(long long a,long long b,long long &x,long long &y){
10     long long d;
11     if(b==0){
12         x=1;y=0;
13         return a;
14     }
15     d=Egcd(b,a%b,y,x);
16     y-=a/b*x;
17     return d;
18 }
19 long long cal(long long a, long long b, long long mod) {//模拟a*b%mod
20     long long ret = 0;
21     while(b) {
22         if(b & 1) ret = (ret + a) % mod;
23         a = (a + a) % mod;
24         b >>= 1;
25     }
26     return ret;
27 }
28 
29 long long c_r(int len){//中国剩余定理
30     int i;
31     long long d,x,y,n,m,ret;
32     ret=0;
33     n=1;
34     for(i=0;i<len;i++)
35         n*=c[i];
36     for(i=0;i<len;i++){
37         m=n/c[i];
38         d=Egcd(m,c[i],y,x);
39         y=(c[i]+y%c[i])%c[i];
40         if( i&1) y -= c[i];
41         //if(y>c[i]-y) y=y-c[i];
42         ret=(ret+cal(y*m, w[i], n))%n;
43     }
44     return (n+ret%n)%n;
45 }
46 
47 void init(){
48     int i;
49     memset(a, 0, sizeof(a));
50     a[0]=1;
51     for(i=1;i<Mod;i++)
52         a[i]=a[i-1]*i%Mod;
53 }
54 
55 long long gcd(long long a,long long b){
56     if(b==0) return a;
57     return gcd(b,a%b);
58 }
59 
60 long long choose(long long n,long long m){
61     if(m>n)return 0;
62     else if(n==m) return 1;
63     long long nn=a[n],mm=a[m]*a[n-m]%Mod;
64     long long d=gcd(nn,mm);
65     nn/=d;
66     mm/=d;
67     long long x,y;
68     Egcd(mm,Mod,x,y);
69     x=(x+Mod)%Mod;
70     return (x*nn)%Mod;
71 }
72 
73 long long work(long long n,long long m){//lucas
74     long long ret=1;
75     while(n&&m){
76         ret*=choose(n%Mod,m%Mod);
77         ret%=Mod;
78         n/=Mod,m/=Mod;
79     }
80     return ret;
81 }
82 
83 int main(){
84     int T;
85     long long n,m;
86     int k;
87     scanf("%d",&T);
88     while(T--){
89         cin >> n >> m >> k;
90         if(m > n-m) m = n-m;
91         for(int i=0;i<k;i++){
92             cin >> c[i];
93             Mod=c[i];
94             init();
95             w[i]=work(n,m);
96         }
97         cout << c_r(k) << endl;
98     }
99 }

 

posted @ 2015-09-13 22:01  beisong  阅读(220)  评论(0编辑  收藏  举报