【BZOJ4870】组合数问题(计数DP,快速幂)
题意:
1 ≤ n ≤ 10^9, 0 ≤ r < k ≤ 50, 2 ≤ p ≤ 2^30 − 1
思路:From http://blog.csdn.net/qq_33229466/article/details/70665582
实际上就是要我们从nk件物品里面选出若干件,使得其数量模k等于r的方案数。
显然的dp方程f[i,j]表示前i件物品拿了若干件使得其数量模k等于j的方案数。
那么显然有f[i,j]=f[i−1,j]+f[i−1,j−1]
矩阵乘法优化即可。
复杂度O(k3logn)
还有一种更棒的做法,同样是dp,但可以发现f[n∗2,i+j]+=f[n,i]∗f[n,j]
可以理解成枚举前n个物品的选法和后n个物品的选法。
那么直接对dp数组做快速幂即可。
复杂度O(k2logn)
1 type arr=array[0..60]of int64; 2 var ans,c,a:arr; 3 n,p,k,r,t:int64; 4 5 procedure dp(var a:arr;b:arr); 6 var i,j:longint; 7 begin 8 for i:=0 to k-1 do c[i]:=0; 9 for i:=0 to k-1 do 10 for j:=0 to k-1 do 11 c[(i+j) mod k]:=(c[(i+j) mod k]+a[i]*b[j] mod p) mod p; 12 for i:=0 to k-1 do a[i]:=c[i]; 13 end; 14 15 begin 16 assign(input,'bzoj4870.in'); reset(input); 17 assign(output,'bzoj4870.out'); rewrite(output); 18 read(n,p,k,r); 19 inc(ans[0]); inc(ans[1 mod k]); 20 a:=ans; t:=n*k-1; 21 while t>0 do 22 begin 23 if t and 1=1 then dp(ans,a); 24 dp(a,a); 25 t:=t>>1; 26 end; 27 writeln(ans[r]); 28 29 close(input); 30 close(output); 31 end.
null