2018.09.29【洛谷P2106】Sam数(数位DP)(矩阵快速幂)

本文探讨了一种使用矩阵快速幂优化数位DP的方法,适用于处理每层转移方程相同的场景。通过构造特定矩阵并利用快速幂进行递推,有效解决数位DP问题,特别是在避免重复计算和提高效率方面。代码示例展示了如何实现这一算法,包括状态转移方程的推导和特殊情况的处理。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

传送门


解析:

其实这种只用位数转移的数位 D P DP DP,大概都可以用矩阵快速幂推。本质原因是每层的转移方程与这是第几层无关,比如这道题

思路:

可以发现一个很显然的情况,就是上面说的,这道题可以矩阵快速幂转移。
不过还是要推一下普通状态转移式子才知道应该怎么构造矩阵。转移方程如下: d p [ i + 1 ] [ j ] = ∑ x = m a x ( j − 2 , 0 ) m i n ( 0 , j + 2 ) d p [ i ] [ x ] dp[i+1][j]=\sum_{x=max(j-2,0)}^{min(0,j+2)}dp[i][x] dp[i+1][j]=x=max(j2,0)min(0,j+2)dp[i][x]
其中 d p [ i ] [ j ] dp[i][j] dp[i][j]表示最高位为 j j j

构造矩阵快速幂递推即可。
注意一个问题, k k k位数是没有前导0的,但是1位数有一个0,所以要特判一下。


代码:

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define re register
#define gc getchar
#define pc putchar
#define cs const

inline
ll getint(){
	re ll num;
	re char c;
	while(!isdigit(c=gc()));num=c^48;
	while(isdigit(c=gc()))num=(num<<1)+(num<<3)+(c^48);
	return num;
}

cs int N=10;
cs ll mod=1000000007;

struct matrix{
	ll a[N][N];
	matrix(int sign=0){
		memset(a,0,sizeof a);
		if(sign)for(int re i=0;i<N;++i)a[i][i]=sign;
	}
	
	ll *cs operator[](cs int&offset){
		return a[offset];
	}
	
	friend matrix operator*(cs matrix &A,cs matrix &B){
		matrix C;
		for(int re i=0;i<N;++i)
		for(int re j=0;j<N;++j)
		for(int re k=0;k<N;++k)
		C[i][j]=(C[i][j]+A.a[i][k]*B.a[k][j]%mod)%mod;
		return C;
	}
	
}A,B,res;

matrix quickpow(matrix a,ll b){
	matrix ans(1);
	while(b){
		if(b&1)ans=ans*a;
		a=a*a;
		b>>=1;
	}
	return ans;
}

ll k;
signed main(){
	k=getint();
	if(k==1){puts("10");return 0;}
	for(int re i=1;i<10;++i)A[0][i]=1;
	for(int re i=0;i<10;++i)
	for(int re j=0;j<10;++j)if(abs(i-j)<=2)
	++B[i][j];
	res=A*quickpow(B,k-1);
	ll ans=0;
	for(int re i=0;i<10;++i)ans=(ans+res[0][i])%mod;
	cout<<ans;
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值