【洛谷P3746】组合数问题

题目

题目链接:https://www.luogu.com.cn/problem/P3746
组合数 \(C_n^m\) 表示的是从 \(n\) 个互不相同的物品中选出 \(m\) 个物品的方案数。举个例子, 从 \((1, 2, 3)\) 三个物品中选择两个物品可以有 \((1, 2)\)\((1, 3)\)\((2, 3)\) 这三种选择方法。根据组合数的定义,我们可以给出计算组合数 \(C_n^m\) 的一般公式:

\[C_n^m = \frac {n!} {m! \ (n - m)!} \]

其中 \(n! = 1 \times 2 \times \cdots \times n\)。(特别地,当 \(n = 0\) 时,\(n! = 1\);当 \(m < n\) 时,\(C_n^m = 0\)。)
小葱在 NOIP 的时候学习了 \(C_i^j\)\(k\) 的倍数关系,现在他想更进一步,研究更多关于组合数的性质。小葱发现,\(C_i^j\) 是否是 \(k\) 的倍数,取决于 \(C_i^j \bmod k\) 是否等于 \(0\),这个神奇的性质引发了小葱对 \(\mathrm{mod}\) 运算(取余数运算)的兴趣。现在小葱选择了是四个整数 \(n, p, k, r\),他希望知道

\[\left( \sum_{i = 0}^\infty C_{nk}^{ik + r} \right) \bmod p, \]

\[\left( C_{nk}^{r} + C_{nk}^{k + r} + C_{nk}^{2k + r} + \cdots + C_{nk}^{(n - 1)k + r} + C_{nk}^{nk + r} + \cdots \right) \bmod p \]

的值。
\(1 \leq n \leq 10^9, 0 \leq r < k \leq 50, 2 \leq p \leq 2^{30} - 1\)

思路

发现题目要求的就是

\[\sum_{i\bmod n=r}\binom{nk}{i}\bmod p \]

发现 \(n\) 很大,但是 \(k,r\) 很小,而我们只需要求对于 \(i\bmod p=r\)\(i\) 的贡献,考虑矩阵乘法。
\(f_{i,j}\) 表示对于 \(\sum_{k\bmod p=j}\binom{i}{k}\) 的和。显然

\[f_{i,j}=f_{i-1,j}+f_{i-1,j-1} \]

注意这里第二维是 \(\bmod p\) 意义下的。
然后矩阵乘法优化就可以做到 \(O(k^3\log (nk))\)

代码

#include <bits/stdc++.h>
#define int long long
using namespace std;
typedef long long ll;

const int N=52;
int n,p,k,r;

struct Matrix
{
	int b[N][N];
	
	friend Matrix operator *(Matrix a,Matrix b)
	{
		Matrix c;
		memset(c.b,0,sizeof(c.b));
		for (int i=0;i<N;i++)
			for (int j=0;j<N;j++)
				for (int k=0;k<N;k++)
					c.b[i][j]=(c.b[i][j]+1LL*a.b[i][k]*b.b[k][j])%p;
		return c;
	}
}a,f;

signed main()
{
	scanf("%lld%lld%lld%lld",&n,&p,&k,&r);
	for (int i=0;i<k;i++)
		a.b[i][i]++,a.b[i][(i+1)%k]++;
	f.b[0][0]=1;
	for (ll i=n*k;i;i>>=1,a=a*a)
		if (i&1) f=f*a;
	printf("%lld",f.b[0][r]);
	return 0;
}
posted @ 2021-02-21 22:48  stoorz  阅读(177)  评论(0)    收藏  举报