BZOJ-2-4870: [Shoi2017]组合数问题 矩阵优化 DP

就 是 要 我 们 从  n k  件 物 品 里 面 选 出 若 干 件,使 得 其 数 量 模 k 等 于 r 的 方 案 数 。
dp方程 f [ i , j ] 表示前 i 件物品拿了若干件使得其数量模 k 等 于 j 的 方 案 数。

非常明显的 i 与 i - 1递推的DP,  可以转化推矩阵,进行矩阵乘法。

那么显然有f [ i , j ] =  f [ i − 1 ,j ]  + f[ i − 1,j − 1 ]  f [  i , j ]= f  [ i  − 1,j ]+ f [ i − 1, j − 1 ]
矩阵乘法优化即可,注意 k等于 1时 矩阵初始化  需要一直 ++而不是赋值为 1。

#include<bits/stdc++.h>
using namespace std;
#define maxn 55
#define ll long long
ll n,mod,k,r;
struct node
{
    ll sx,sy;
    ll num[maxn][maxn];
    void up1()
    {
        for(int i=0; i<sx; i++)
            for(int j=0; j<sy; j++)
                num[i][j]=0;
    }
} A,ans;
node operator*(node a,node b)
{
    node c;
    c.sx=a.sx,c.sy=b.sy;
    c.up1();
    for(int i=0; i<a.sx; i++)
        for(int j=0; j<b.sy; j++)
            for(int q=0; q<a.sy; q++)
                c.num[i][j]=(c.num[i][j]%mod+a.num[i][q]%mod*b.num[q][j]%mod)%mod;
    return c;
}
void qpow(ll b)
{
    while(b)
    {
        if(b%2)
            ans=ans*A;
        A=A*A;
        b>>=1;
    }
}
int main()
{
    scanf("%lld%lld%lld%lld",&n,&mod,&k,&r);
    ans.sx=1;
    ans.sy=A.sx=A.sy=k;
    A.up1();
    ans.up1();
    ans.num[0][0]=1;
    for(int i=0; i<k; i++)
    {
        A.num[i][i]=1;
        A.num[i][(i+1)%k]++;
    }
    qpow(n*k);
    printf("%lld
",ans.num[0][r]);
    return 0;
}

  

原文地址:https://www.cnblogs.com/SDUTNING/p/10241334.html