[六省联考2017]组合数问题 (矩阵优化$dp$)

题目链接

Solution

矩阵优化 \(dp\).

题中给出的式子的意思就是:

求 nk 个物品中选出 mod k 为 r 的个数的物品的方案数.

考虑朴素 \(dp\) ,定义状态 \(f[i][j]\) 代表前 \(i\) 个物品选择 \(mod~k\) 为 \(j\) 的方案数.

那么转移方程也很简单 :

\[f[i][j]_{j\in[1,i)}=f[i-1][j]+f[i-1][(j-1+k)mod~k]
\]

但是很显然这样是 \(O(n^2k)\) .

考虑优化,发现对于每一项状态,仅与 \(i-1\) 的状态有关.

如此我们可以考虑构建一个 \(k*k\) 的转移矩阵,即:

\[ \begin{matrix}
mod~k= &0 &1 &2 &...&k-1\\
&1 & 0 &0&...& 1 \\
&1 &1&0 &... &0 \\
&0 & 1 &1 &... & 0\end{matrix}
\tag{1}
\]

然后初始矩阵即为一个 \(k*1\) 的矩阵.

然后就可以矩阵快速幂了.

Code

#include<bits/stdc++.h>
#define in(x) x=read()
#define ll long long
using namespace std; int read()
{
char ch=getchar(); int w=0;
while(ch<'0'||ch>'9')ch=getchar();
while(ch>='0'&&ch<='9'){w=w*10+ch-'0';ch=getchar();}
return w;
} ll n,mod,k,r;
struct Matrix{
ll a[51][51];
}; Matrix X(Matrix s,Matrix e)
{
Matrix c;
memset(c.a,0,sizeof(c.a));
for(int i=0;i<k;i++)
for(int j=0;j<k;j++)
for(int l=0;l<k;l++)
{
c.a[i][j]+=(s.a[i][l]*e.a[l][j])%mod;
c.a[i][j]%=mod;
}
return c;
} Matrix quick_pow(Matrix s,ll ks)
{
if(ks==1)return s;
Matrix k=s; ks--;
while(ks>0)
{
if(ks%2==1)k=X(k,s);
ks/=2;
s=X(s,s);
}
return k;
} Matrix x(Matrix s,Matrix e)
{
Matrix c;
memset(c.a,0,sizeof(c.a));
for(int i=0;i<1;i++)
for(int j=0;j<k;j++)
for(int l=0;l<k;l++)
{
c.a[j][i]+=(s.a[j][l]*e.a[l][i])%mod;
c.a[j][i]%=mod;
}
return c;
} int main()
{
in(n),in(mod),in(k),in(r);
Matrix P,f;
memset(P.a,0,sizeof(P.a));
memset(f.a,0,sizeof(f.a));
for(ll i=0;i<k;i++)
{
if(i==0)
P.a[i][0]++,P.a[i][k-1]++;
else
P.a[i][i-1]++,P.a[i][i]++;
}
P=quick_pow(P,n*k);
f.a[0][0]=1;
f=x(P,f);
cout<<f.a[r%k][0]%mod<<endl;
}
上一篇:洛谷P3193 GT考试 kmp+矩阵优化dp


下一篇:$[TJOI2017]$ 可乐 矩阵优化$dp$