[六省联考2017]组合数问题

做题时间:2022.7.6

\(【题目描述】\)

给定正整数 \(n,p,k,r(1\leq n\leq 10^9,0\leq r<k\leq 50,2\leq p\leq 2^{30}-1)\) ,求:

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

\(【输入格式】\)

一行四个整数 \(n,p,k,r\)

\(【输出格式】\)

一行一个整数表示答案

\(【考点】\)

数学、组合、递推、矩阵快速幂

\(【做法】\)

直接用代数方法硬凑行不通,各个项模 \(p\) 的余数也没有规律,可以考虑其实际意义

\(C_{nk}^{ik+r}\) 表示 \(nk\) 个物品里选取 \(ik+r\) 个的方案数, 而 \(ik+r(0\leq i< \inf)\) 非常类似于一个带余除法的逆运算,原式中 \(i\) 一直在变化,可以看成商,对应地 \(k\) 可以看成除数, \(r\) 可以看成余数。这样原式就有了实际意义——在 \(nk\) 个物品中选出 \(\mod k\)\(r\) 个数的总方案数(其中的每一项就代表了 \(\mod k\) 不同的商)

重新定义: \(f_{i,j}\) 表示在 \(nk\) 个物品中选出 \(\mod k\)\(r\) 个数的总方案数,也就是原式,类似于组合数的递推,考虑第 \(i\) 个数有选和不选两种情况,选的方案数为 \(f_{i-1,j-1}\) ,不选的方案数为 \(f_{i-1,j}\) ,递推式就是:

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

特殊情况是,当 \(j=0\) 时,转移方程变为:

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

要求的就是 \(f_{nk,r}\)

由于 \(nk\) 很大,考虑矩阵加速,每一个 \(f_{i}\) 都与 \(f_{i-1}\) 有关,因此考虑从 \(f_{0,j}\) 推至 \(f_{nk,j}\) ,其中 \(0\leq j <k\) (因为 \(f_{i,0}\)\(f_{i-1,k}\) 相关且 \(r<k\) )。

这样就可以求出方程了。

另外要注意一点,当 \(k=1\) 时,矩阵就变成一个数了,此时底数矩阵应当为 \([2]\) ,这个点直接将赋初值变成累加即可。

\[\begin{bmatrix} f_{0,0}\\ f_{0,2}\\ ......\\ f_{0,k-1} \end{bmatrix}\times \begin{bmatrix} 1&0&0&......&1\\ 1&1&0&...... &0\\ ......\\ 0 & 0&......&1&1 \end{bmatrix}^{nk} = \begin{bmatrix} f_{nk,0}\\ f_{nk,1}\\ ......\\ f_{nk,k-1} \end{bmatrix} \]

其中 \(f_{0,0}=1,f_{0,i}=0(1\leq i\leq k-1)\)

\(【代码】\)

#include<cstdio>
#include<cstring>
using namespace std;
const int N=55;
typedef long long ll;
ll n,p,K,R;
struct Matrix{
	ll a[N][N];
	
	Matrix(){memset(a,0,sizeof(a)); }
	
	Matrix operator *(const Matrix b) const{
		Matrix c;
		for(int i=0;i<K;i++){
			for(int j=0;j<K;j++){
				for(int k=0;k<K;k++){
					c.a[i][j]=(c.a[i][j]+a[i][k]*b.a[k][j]%p)%p;
				}
			}
		}
		return c;
	}
}A,B,One;
void Init()
{
	for(int i=0;i<K;i++) One.a[i][i]=1;
	B.a[0][0]=1;
	for(int i=0;i<K;i++) A.a[i][(i-1+K)%K]++,A.a[i][i]++;
  //当k=1时,(i-1+k)%k=i, a[i][i]=2
  //当i=0时,(i-1+k)%k=k-1
}
Matrix FastPow(Matrix x,ll b)
{
	Matrix ans=One;
	while(b){
		if(b&1) ans=ans*x;
		x=x*x;
		b>>=1;
	}
	return ans;
}
int main()
{
	scanf("%d%d%d%d",&n,&p,&K,&R);
	Init();
	
	Matrix ans=FastPow(A,(ll)n*K);

	printf("%lld\n",ans.a[0][R]);
	return 0;
}
posted @ 2022-07-06 15:31  lxzy  阅读(37)  评论(0)    收藏  举报