[HNOI2011]数学作业


矩阵乘法优化dp

O(n) 递推十分显然

\(f[i] = f[i - 1] * 10 ^ {lg10(i)} + i\)

然后考虑矩乘

初始矩阵就是

\[\begin{Bmatrix} 1 & 1 & 1 \end{Bmatrix} \]

然后转移矩阵是

\[ \begin{Bmatrix} 10^k & 0 & 0 \\ 1 & 1 & 0 \\ 1 & 1 & 1 \end{Bmatrix} \]

我们发现一个问题

就是转移矩阵的第\((1,1)\)位是会变化的

所以我们按照十进制下的每一位单独建转移矩阵跑矩乘

每次更新转移矩阵后都用初始矩阵去乘

思路说起来好像挺简单

然后一堆细节让我调了半个下午

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
# define int unsigned long long
using namespace std ;
int n , mod , pw[25] ;

struct Mat {
	int f[4][4] ;
	inline void clear() { memset(f , 0 , sizeof(f)) ; }
	inline void Begin() 
	{ for(int i = 1 ; i <= 3 ; i ++) f[i][i] = 1 ; }
} st , a ; 
inline Mat operator * (Mat x , Mat y) {
	Mat temp ; memset(temp.f , 0 , sizeof(temp.f)) ;
	for(int i = 1 ; i <= 3 ; i ++)
	    for(int j = 1 ; j <= 3 ; j ++)
	        for(int k = 1 ; k <= 3 ; k ++)
	            temp.f[i][j] = (temp.f[i][j] + x.f[i][k] * y.f[k][j]) % mod ;
	return temp ;
}
inline Mat fpw(Mat Base , int k) {
	Mat temp = st ;
	while(k) {
		if(k & 1) temp = temp * Base ;
		Base = Base * Base ; k >>= 1 ;
	}
	return temp ;
}
inline int Solve() {
	st.f[1][1] = st.f[1][2] = st.f[1][3] = 1 ;
	for(int w = 10 , pre = 0 ; pre < n ; w *= 10) {
		a.f[1][1] = w % mod ; a.f[1][2] = 0 ; a.f[1][3] = 0 ;
		a.f[2][1] = 1 ; a.f[2][2] = 1 ; a.f[2][3] = 0 ;
		a.f[3][1] = 1 ; a.f[3][2] = 1 ; a.f[3][3] = 1 ;
		st = fpw(a , min(w - 1 , n) - pre - (w <= 10)) ; pre = w - 1 ;
	}
	return st.f[1][1] ;
}
# undef int
int main() {
# define int long long
	cin >> n >> mod ;
	cout << Solve() << endl ;
	return 0 ;
}
posted @ 2018-10-11 15:08  beretty  阅读(207)  评论(0编辑  收藏  举报