bzoj4870 [Shoi2017]组合数问题

传送门: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][N], 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

 

posted @ 2017-04-25 20:39  Galaxies  阅读(607)  评论(0编辑  收藏  举报