【BZOJ4870】[Shoi2017]组合数问题 动态规划(矩阵乘法)

【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;
}
posted @ 2017-05-15 10:09  CQzhangyu  阅读(328)  评论(0编辑  收藏  举报