bzoj 4870: [Shoi2017]组合数问题 [矩阵乘法优化dp]

4870: [Shoi2017]组合数问题

题意:求

\[\sum_{i=0}^{n-1} \binom{nk}{ik+r} \mod p \]

\(n \le 10^9, 0\le r < k \le 50\)


组合数推了一下,有一些有趣的性质但是并不好做

想到了从意义方面考虑,但是没有深入,去看了题解


n大k小,一副矩乘的样子

就是求“n个物品取模k余r个的方案数”

因为取的个数模k,变得很有意思,可以把组合数的递推式矩乘了...

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long ll;
inline int read() {
	char c=getchar(); int x=0,f=1;
	while(c<'0' || c>'9') {if(c=='-')f=-1; c=getchar();}
	while(c>='0' && c<='9') {x=x*10+c-'0'; c=getchar();}
	return x*f;
}

int n, mo, k, r;
struct matrix {
	int a[51][51];
	matrix() {memset(a, 0, sizeof(a));}
	int* operator [](int x) {return a[x];}
} f, a;
matrix operator *(matrix a, matrix b) {
	int n = k;
	matrix c;
	for(int i=0; i<n; i++)
		for(int k=0; k<n; k++) if(a[i][k])
			for(int j=0; j<n; j++) if(b[k][j])
				c[i][j] = (c[i][j] + (ll) a[i][k] * b[k][j] %mo) %mo;
	return c;
}
matrix operator ^(matrix a, ll b) {
	int n = k;
	matrix c; 
	for(int i=0; i<n; i++) c[i][i] = 1;
	for(; b; b>>=1, a=a*a) if(b&1) c=c*a;
	return c;
}

int Pow(ll a, int b) {
	ll ans=1;
	for(; b; b>>=1, a=a*a%mo)
		if(b&1) ans=ans*a%mo;
	return ans;
}
int main() {
	freopen("in", "r", stdin);
	n=read(); mo=read(); k=read(); r=read();
	if(k == 1) {printf("%d", Pow(2, n)); return 0;}
	for(int i=0; i<k; i++) f[i][i] = 1, f[(i+1)%k][i] = 1;
	f = f ^ ((ll) n * k);
	a[0][0] = 1;
	a = f * a;
	printf("%d\n", a[r][0]);
}

posted @ 2017-04-26 21:42  Candy?  阅读(460)  评论(0编辑  收藏  举报