带模矩阵快速幂模板
#include <iostream>
#include <algorithm>
#include <map>
#include <stdio.h>
#include <string.h>
#include <unordered_map>
#include <vector>
#include <queue>
#include <set>
#include <math.h>
using namespace std;
const int mod = 998244353 ;
typedef long long ll;
struct mat
{
ll m[ 14 ] [ 14 ] ;
} ;
mat unit;
mat matmul ( mat a, mat b)
{
mat c;
ll tmp = 0 ;
for ( int i= 0 ; i< 14 ; i++ )
for ( int j= 0 ; j< 14 ; j++ )
{
tmp= 0 ;
for ( int k= 0 ; k< 14 ; k++ )
{
tmp= ( tmp+ a. m[ i] [ k] * b. m[ k] [ j] ) % mod;
}
c. m[ i] [ j] = tmp;
}
return c;
}
struct mat QuickPow ( struct mat a, ll n)
{
for ( int i= 0 ; i< 14 ; i++ )
unit. m[ i] [ i] = 1 ;
mat ans= unit;
while ( n)
{
if ( n& 1 )
ans= matmul ( ans, a) ;
a = matmul ( a, a) ;
n>>= 1 ;
}
return ans;
}
理解:每次乘以幂次n中二进制bit为1的位的权系数的a幂次;
96分解法
对|S|=1只需统计每个数值。1可生成2,2可生成4,4可生成1个6,1个1,6可生成1个6,1个4; 对|S|=2,两种情况 :
这个两位数由一位数生成。例如4生成16,6生成64; 这个两位数由一个两位数生成 ,且是这个用来生成两位数前一个数字生成数字的最后一个数字和后一个数字生成数字的最前一个数字。例如对于两位数16,1生成2,6生成64. 26是目标结果。 这样对1,2,4,6可以组成的所有两位数,考察其可以转移到的两位数 ,并去除完全不能从前一个状态转移到的所有两位数(即不可能出现的两位数) 对所有可能的长度为1的数字和长度为2的数字赋予一个编号,串在n秒后含有的这些数字为dp[n][i],其中i是该数字的编号。 于是dp[n][i]仅仅是dp[n-1]向量分量的线性组合,可以使用矩阵表示 。可以使用矩阵快速幂快速求解
#include <iostream>
#include <algorithm>
#include <map>
#include <stdio.h>
#include <string.h>
#include <unordered_map>
#include <vector>
#include <queue>
#include <set>
#include <math.h>
using namespace std;
const int mod = 998244353 ;
typedef long long ll;
struct mat
{
ll m[ 14 ] [ 14 ] ;
} ;
mat unit;
mat matmul ( mat a, mat b)
{
mat c;
ll tmp = 0 ;
for ( int i= 0 ; i< 14 ; i++ )
for ( int j= 0 ; j< 14 ; j++ )
{
tmp= 0 ;
for ( int k= 0 ; k< 14 ; k++ )
{
tmp= ( tmp+ a. m[ i] [ k] * b. m[ k] [ j] ) % mod;
}
c. m[ i] [ j] = tmp;
}
return c;
}
struct mat QuickPow ( struct mat a, ll n)
{
for ( int i= 0 ; i< 14 ; i++ )
unit. m[ i] [ i] = 1 ;
mat ans= unit;
while ( n)
{
if ( n& 1 )
ans= matmul ( ans, a) ;
a = matmul ( a, a) ;
n>>= 1 ;
}
return ans;
}
int main ( )
{
int n;
string s;
cin >> n;
cin >> s;
unordered_map< int , int > a;
a[ 2 ] = 1 ; a[ 4 ] = 2 ; a[ 1 ] = 0 ; a[ 6 ] = 3 ; a[ 16 ] = 4 ; a[ 26 ] = 5 ; a[ 46 ] = 8 ; a[ 66 ] = 12 ; a[ 44 ] = 7 ;
a[ 41 ] = 6 ; a[ 61 ] = 9 ; a[ 42 ] = 13 ; a[ 62 ] = 10 ; a[ 64 ] = 11 ;
struct mat ma;
for ( int i = 0 ; i< 14 ; i++ ) for ( int j = 0 ; j< 14 ; j++ ) ma. m[ i] [ j] = 0 ;
ma. m[ a[ 2 ] ] [ a[ 1 ] ] = 1 ;
ma. m[ a[ 4 ] ] [ a[ 2 ] ] = 1 ; ma. m[ a[ 4 ] ] [ a[ 6 ] ] = 1 ;
ma. m[ a[ 1 ] ] [ a[ 4 ] ] = 1 ;
ma. m[ a[ 6 ] ] [ a[ 4 ] ] = 1 ; ma. m[ a[ 6 ] ] [ a[ 6 ] ] = 1 ;
ma. m[ a[ 16 ] ] [ a[ 4 ] ] = 1 ;
ma. m[ a[ 26 ] ] [ a[ 16 ] ] = 1 ;
ma. m[ a[ 46 ] ] [ a[ 66 ] ] = 1 ; ma. m[ a[ 46 ] ] [ a[ 26 ] ] = 1 ;
ma. m[ a[ 66 ] ] [ a[ 46 ] ] = 1 ;
ma. m[ a[ 44 ] ] [ a[ 62 ] ] = 1 ;
ma. m[ a[ 41 ] ] [ a[ 64 ] ] = 1 ;
ma. m[ a[ 61 ] ] [ a[ 44 ] ] = 1 ;
ma. m[ a[ 42 ] ] [ a[ 61 ] ] = 1 ;
ma. m[ a[ 62 ] ] [ a[ 41 ] ] = 1 ;
ma. m[ a[ 64 ] ] [ a[ 6 ] ] = 1 ; ma. m[ a[ 64 ] ] [ a[ 42 ] ] = 1 ;
ma = QuickPow ( ma, n) ;
int k= 0 ;
if ( s. size ( ) == 2 ) k+ = ( s[ 0 ] - '0' ) * 10 + ( s[ 1 ] - '0' ) ;
else k = s[ 0 ] - '0' ;
cout << ( ma. m[ a[ k] ] [ 0 ] ) << endl;
system ( "pause" ) ;
return 0 ;
}
DP思考的两种方式
考察dp结点拓扑图。可以在拓扑排序过程中直接在当前结点计算后继结点的值。也可以固定一个结点看进入该结点的边对应的另一个结点。