【BZOJ2142】礼物(扩展lucas定理,中国剩余定理合并方程)
题意:有n件礼物,m个人,每个人分别需要w[i]件礼物,求分礼物的不同方案数 mod P
提示:设P=p1^c1 * p2^c2 * p3^c3 * … *pt ^ ct,pi为质数。
1≤n≤10^9,1≤m≤5,1≤pi^ci≤10^5。
P不一定为质数
思路:经推导答案即为n!/(w[i]!),i=1..n
考虑P不是质数
将P分解为提示中所说的形式,可以发现所有pt^ct都是互质的,所以我们可以用下图的中国剩余定理合并
From http://blog.csdn.net/popoqqq/article/details/39891263
然后对于每个pi^ai,我们进行以下处理:
将分子和分母化为x*pi^y的形式
然后分母的x部分与pi互质,可以求逆元,分子分母的y部分直接相减即可
然后我们处理阶乘
以19为例,将19!化为x*pi^y的形式,其中pi=3,ai=2 则有
19!%9=(1*2*3*4*5*6*7*8*9*10*11*12*13*14*15*16*17*18*19) %9
=(1*2*4*5*7*8*10*11*13*14*16*17*19)*3^6*(1*2*3*4*5*6) %9
式子的左半部分是不为3的倍数的数,存在长度为p^a的循环节 求出一个循环节 快速幂处理 然后处理剩余部分
右半部分是6!%9 递归处理即可
用这样的思路就可以换解决C(n,m) mod p,p不为质数的情况了
1 var a:array[1..1000]of int64; 2 n,m,i:longint; 3 ans,s,mo:int64; 4 5 function mult(x,y,p:int64):int64; 6 var tmp:int64; 7 begin 8 mult:=1; tmp:=x; 9 while y>0 do 10 begin 11 if y and 1=1 then mult:=mult*tmp mod p; 12 tmp:=tmp*tmp mod p; 13 y:=y>>1; 14 end; 15 end; 16 17 procedure exgcd(a,b:int64;var d,x,y:int64); 18 begin 19 if b=0 then 20 begin 21 d:=a; x:=1; y:=0; 22 end 23 else 24 begin 25 exgcd(b,a mod b,d,y,x); 26 y:=y-(a div b)*x; 27 end; 28 end; 29 30 function inv(a,n:int64):int64; 31 var d,x,y:int64; 32 begin 33 exgcd(a,n,d,x,y); 34 if d=1 then exit((x+n) mod n); 35 exit(-1); 36 end; 37 38 function fac(n,p,pr:int64):int64; 39 var i,re,r:int64; 40 begin 41 if n=0 then exit(1); 42 re:=1; i:=2; 43 while i<=pr do 44 begin 45 if i mod p>0 then re:=re*i mod pr; 46 inc(i); 47 end; 48 49 re:=mult(re,n div pr,pr); 50 r:=n mod pr; 51 i:=2; 52 while i<=r do 53 begin 54 if i mod p>0 then re:=re*i mod pr; 55 inc(i); 56 end; 57 58 exit(re*fac(n div p,p,pr) mod pr); 59 end; 60 61 function c(n,m,p,pr:int64):int64; 62 var x,y,z,t,tmp:int64; 63 begin 64 if n<m then exit(0); 65 x:=fac(n,p,pr); 66 y:=fac(m,p,pr); 67 z:=fac(n-m,p,pr); 68 c:=0; 69 t:=n; 70 while t>0 do 71 begin 72 c:=c+t div p; 73 t:=t div p; 74 end; 75 t:=m; 76 while t>0 do 77 begin 78 c:=c-t div p; 79 t:=t div p; 80 end; 81 t:=n-m; 82 while t>0 do 83 begin 84 c:=c-t div p; 85 t:=t div p; 86 end; 87 tmp:=x*inv(y,pr) mod pr*inv(z,pr) mod pr*mult(p,c,pr) mod pr; 88 exit(tmp*(mo div pr) mod mo*inv(mo div pr,pr) mod mo); 89 end; 90 91 92 function lucas(n,m:int64):int64; 93 var x,re,i,pr:int64; 94 begin 95 i:=2; x:=mo; re:=0; 96 while i<=x do 97 begin 98 if x mod i=0 then 99 begin 100 pr:=1; 101 while x mod i=0 do 102 begin 103 x:=x div i; pr:=pr*i; 104 end; 105 re:=(re+c(n,m,i,pr)) mod mo; 106 end; 107 inc(i); 108 end; 109 exit(re); 110 end; 111 112 begin 113 assign(input,'bzoj2142.in'); reset(input); 114 assign(output,'bzoj2142.out'); rewrite(output); 115 readln(mo); 116 readln(n,m); 117 for i:=1 to m do 118 begin 119 read(a[i]); s:=s+a[i]; 120 end; 121 if s>n then 122 begin 123 writeln('Impossible'); 124 close(input); 125 close(output); 126 exit; 127 end; 128 ans:=1; 129 for i:=1 to m do 130 begin 131 ans:=ans*lucas(n,a[i]) mod mo; 132 n:=n-a[i]; 133 end; 134 writeln(ans); 135 136 close(input); 137 close(output); 138 end.
null