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

【BZOJ4870】[Shoi2017]组合数问题

Description

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

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;
}
上一篇:1.JAVA中使用JNI调用C++代码学习笔记


下一篇:Java 中 try、catch、finally 语句块的执行顺序