http://www.elijahqi.win/archives/531
题目描述
组合数 CnmC_n^mCnm 表示的是从 n 个互不相同的物品中选出 m 个物品的方案数。举个例子,从 (1;2;3) 三个物品中选择两个物品可以有 (1;2);(1;3);(2;3) 这三种选择方法。根据组合数的定义,我们可以给出计算组合数 CnmC_n^mCnm 的一般公式:
Cnm=n!m!(n−m)!C_n^m = \frac{n!}{m!(n-m)!}Cnm=m!(n−m)!n!
其中 n! = 1 × 2 × · · · × n。(特别的,当 n = 0 时, n! = 1 ,当 m > n 时, Cnm=0C_n^m =0Cnm=0)
小葱在 NOIP 的时候学习了 CijC_i^jCij 和 k 的倍数关系,现在他想更进一步,研究更多关于组合数的性质。小葱发现, CijC_i^jCij 是否是 k 的倍数,取决于 CijmodkC_i^j mod k Cijmodk是否等于 0,这个神奇的性质引发了小葱对 mod 运算(取余数)的兴趣。现在小葱选择了是四个整数n; p; k; r,小葱现在希望知道
∑i=0infCnkik+rmodp\sum_{i=0}^{\inf} C_{nk}^{ik+r} mod p∑i=0infCnkik+rmodp
的值。
输入输出格式
输入格式:
第一行有四个整数 n; p; k;r,所有整数含义见问题描述。
输出格式:
一行一个整数代表答案。
输入输出样例
输入样例#1:
2 10007 2 0
输出样例#1:
8
输入样例#2:
20 10007 20 0
输出样例#2:
176
说明
• 对于 30% 的测试点, 1 ≤ n; k ≤ 30, p 是质数;
• 对于另外 5% 的测试点, p = 2;
• 对于另外 5% 的测试点, k = 1;
• 对于另外 10% 的测试点, k = 2;
• 对于另外 15% 的测试点, 1 ≤ n ≤ 10^3; 1 ≤ k ≤ 50, p 是质数;
• 对于另外 15% 的测试点, 1 ≤ n × k ≤ 10^6, p 是质数;
• 对于另外 10% 的测试点, 1 ≤ n ≤ 10^9; 1 ≤ k ≤ 50, p 是质数;
• 对于 100% 的测试点, 1 ≤ n ≤ 10^9; 0 ≤ r < k ≤ 50; 2 ≤ p ≤ 2^30 − 1。
需要看出来这个公式的实际意义,因为r
#include<cstdio>
#include<cstring>
#define N 55
struct matrix{
int f[N][N],l,c;
}ans,m;
int n,p,k,r;
inline matrix multiply(matrix a,matrix b){
matrix c;memset(c.f,0,sizeof(c.f));
c.l=a.l;c.c=b.c;
for (int i=0;i<a.l;++i)
for (int j=0;j<b.c;++j)
for (int z=0;z<a.c;++z){
c.f[i][j]=((long long) a.f[i][z]*b.f[z][j]%p+c.f[i][j])%p;
}
return c;
}
int main(){
// freopen("3746.in","r",stdin);
scanf("%d%d%d%d",&n,&p,&k,&r);long long t=(long long)n*k;
m.c=m.l=k;
for (int i=0;i<k;++i) m.f[i][i]++,m.f[(i-1+k)%k][i]++;
ans.c=k;ans.l=k;
for (int i=0;i<k;++i) ans.f[i][i]=1;
for (;t;t>>=1,m=multiply(m,m)){
if (t&1) ans=multiply(m,ans);
}
printf("%d",ans.f[0][r]);
return 0;
}