【BZOJ4870】[Shoi2017]组合数问题
Description
Input
第一行有四个整数 n, p, k, r,所有整数含义见问题描述。
1 ≤ n ≤ 10^9, 0 ≤ r < k ≤ 50, 2 ≤ p ≤ 2^30 − 1
Output
一行一个整数代表答案。
Sample Input
2 10007 2 0
Sample Output
8
题解:题意:nk个数,选出一些数,使得选出来的数的个数%k=r的方案数(所以就不要管原来的题面了!)
然后这变成了一道动态规划题,由于nk很大我们试着用矩阵乘法,发现很容易就能构造出转移矩阵,具体不说了
当然,如果不强行使用矩乘的话也是可以搞的,设f[i][j]表示i个数,取出一些数使得个数%k=j的方案数,然后可以得到转移方程
f[i*2][(j+j')%k]+=f[i][j]*f[i][j']
显然这个式子是满足可加性的,所以可以直接用倍增的思想搞一搞,时间复杂度比矩乘还少一个n
#include <cstdio> #include <cstring> #include <iostream> using namespace std; typedef long long ll; ll n,p; ll k,r; typedef struct matrix { ll v[60]; }M; M x,ans,emp; M mpls(M a,M b) { M c=emp; int i,j; for(i=0;i<k;i++) for(j=0;j<k;j++) c.v[(i+j)%k]=(c.v[(i+j)%k]+a.v[i]*b.v[j])%p; return c; } void pm(ll y) { while(y) { if(y&1) ans=mpls(ans,x); x=mpls(x,x),y>>=1; } } int main() { scanf("%lld%lld%d%d",&n,&p,&k,&r); ans.v[0]=1,x.v[0]=1,x.v[1%k]++; pm(n*k); printf("%lld",ans.v[r]); return 0; }