您的位置:首页 > 其它

bzoj4870 [Shoi2017]组合数问题

2017-04-25 20:39 387 查看
传送门:http://www.lydsy.com/JudgeOnline/problem.php?id=4870

【题解】

题目给了提示了:组合意义

题目转化为从nk个物品中选出模k余r个数的方案数。

f[i,j]表示前i个物品选出模k余j个数的方案数。

矩乘即可。

# include <stdio.h>
# include <assert.h>
# include <string.h>
# include <algorithm>
// # include <bits/stdc++.h>

using namespace std;

typedef long long ll;
typedef long double ld;
typedef unsigned long long ull;
const int M = 5e5 + 10, N = 55;

# define RG register
# define ST static

// nk个物品模k余数r的方案数
int n, mod, k, r;
struct matrix {
int a

, n, m;
inline void set(int _n, int _m) {
n = _n, m = _m;
memset(a, 0, sizeof a);
}
friend matrix operator*(matrix a, matrix b) {
matrix c; assert(a.m == b.n);
c.set(a.n, b.m);
for (int i=1; i<=c.n; ++i)
for (int j=1; j<=c.m; ++j)
for (int k=1; k<=a.m; ++k) {
c.a[i][j] += 1ll * a.a[i][k] * b.a[k][j] % mod;
if(c.a[i][j] >= mod) c.a[i][j] -= mod;
}
return c;
}
friend matrix operator^(matrix a, ll b) {
matrix I; assert(a.n==a.m);
I.set(a.n, a.m);
for (int i=1; i<=a.n; ++i) I.a[i][i] = 1;
while(b) {
if(b&1) I=I*a;
a=a*a;
b>>=1;
}
return I;
}
}A, T;

// f[i,j]表示前i个物品选模k余j个的方案数

int main() {
scanf("%d%d%d%d", &n, &mod, &k, &r);
T.set(k, k); A.set(k, 1);
A.a[1][1] = 1;
for (int i=0; i<k; ++i) {
T.a[i+1][i+1] ++;
T.a[(i-1+k)%k+1][i+1]++;
}
ll t = (ll)n*k;
A = (T^t)*A;
printf("%d\n", A.a[r+1][1]);
return 0;
}


View Code
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: