P5343 【XR-1】分块(dp矩阵加速)

\(大意是用数组a里的数字,组成一个序列,使得序列和为n的方案种数\)传送门

\(先考虑dp.\)

\(但是不能直接用背包转移,因为是序列,要考虑顺序。\)

\(所以,为了去重,我们令dp[i][j]为凑成i最后用的a[j]的方案数\)

dp[0]=1;//把第二维优化掉
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
    if(i>=a[j])
        dp[i]+=dp[i-a[j]];

\(接下来考虑用矩阵加速。\)

\(设a数组中最大的数是size,那么dp[i]最多从dp[i-size]转移过来,所以矩阵大小是size*size\)

现在我们想用

\[\left[ \begin{matrix} dp_{size-1}\\ dp_{size-2}\\ dp_{size-3}\\ ....\\ 1\\ 0\\ \end{matrix} \right] 得到 \left[ \begin{matrix} dp_{size}\\ dp_{size-1}\\ dp_{size-2}\\ ....\\ 1\\ 0\\ \end{matrix} \right] \]

那我们的构造矩阵是怎样的呢?

\(第一行因为dp[i]可以从每一个i-a[j]得到,所以所有mat[1][a[j]]=1;\)

\(其余行,只需要把mat[i][i-1]设置成1即可(下面的构造矩阵省略了第一行,因为是根据具体数据填写)\)

\[\left[ \begin{matrix} dp_{size-1}\\ dp_{size-2}\\ dp_{size-3}\\ ....\\ 1\\ 0\\ \end{matrix} \right] * \left[ \begin{matrix} 0&1&0&0&...&0\\ 0&0&1&0&...&0\\ 0&0&0&1&...&0\\ 0&0&0&0&...&1\\ \end{matrix} \right] = \left[ \begin{matrix} dp_{size}\\ dp_{size-1}\\ dp_{size-2}\\ ....\\ 1\\ 0\\ \end{matrix} \right] \]

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll mod=1e9+7;
ll n,pr,x,nf,a[109],dp[109],size,b[109],c[109];
struct rce{
	ll m[102][102];
	rce(){memset(m,0,sizeof(m));}
};
rce operator * (rce a,rce b)
{
	rce c;
	for(int i=1;i<=size;i++)
	for(int j=1;j<=size;j++)
	{
		c.m[i][j]=0;
		for(int k=1;k<=size;k++)
			c.m[i][j]=(c.m[i][j]+a.m[i][k]*b.m[k][j]%mod)%mod;
	}
	return c;
}
rce quickpow(rce a,ll n)
{
	rce ans;
	for(int i=1;i<=size;i++)
	for(int j=1;j<=size;j++)
	if(i==j)	ans.m[i][j]=1;
	else	ans.m[i][j]=0;
	while(n)
	{
		if(n&1)	ans=ans*a;
		a=a*a;
		n>>=1;
	}
	return ans;
}
int main()
{
	cin>>n;
	cin>>pr;
	for(int i=1;i<=pr;i++)
	{
		cin>>x;
		a[x]=1;
	}
	cin>>nf;
	for(int i=1;i<=nf;i++)
	{
		cin>>x;
		c[x]=1;
	}
	for(ll i=1;i<=100;i++)
	{
		if(a[i]&&c[i])
		{
			b[++b[0]]=i;
			size=max(size,i);
		}
	}
	dp[0]=1;
	for(int i=1;i<=size;i++)
	for(int j=1;j<=b[0];j++)
	{
		if(i>=b[j])
			dp[i]=(dp[i]+dp[i-b[j]])%mod;
	}
	rce zao,init;
	for(int i=1;i<=size;i++)	init.m[i][1]=dp[size-i];
	for(int i=1;i<=b[0];i++)	zao.m[1][b[i]]=1;
	for(int i=2;i<=size;i++)	zao.m[i][i-1]=1;
	zao=quickpow(zao,n-size+1)*init;
	cout<<zao.m[1][1]%mod;
}
posted @ 2020-04-13 22:24  倾叶子佮  阅读(154)  评论(0编辑  收藏  举报