题目大意
有(n)个数,现在把它们划分成若干段,使存在一段的数的个数不为给出的(k)个数中的任意一个。问有多少种划分方式
(nle10^{18})
给出的(k)个数任意一个保证不超过(100)
分析
一个很明显的做法:先求出没有限制条件的总方案数
为(C^1_{n-1}+C^2_{n-1}+C^3_{n-1}+dots+C^{n-1}_{n-1}+1)
这个值就是(2^{n-1})
接下来考虑减去特殊的情况
特殊情况即为:对于每个划分出来的段,个数都是给出的(k)个数之中的其中一个
考虑(DP)
设(f_i)表示划分到第(i)个数,共有几种情况
转移方程即为:
[f_i=sumlimits_{j=1}^{k}{f_{i-t_j}}
]
看到(n)特别大,想到矩乘。
构建状态矩阵
(egin{bmatrix}f_i&f_{i-1}&f_{i-2}&dots&f_{i-100}end{bmatrix})
转移矩阵
第一列(t_i)位为(1),其余为(0),
其他列只有(i-1)位为(1),其余为(0)
#include <bits/stdc++.h>
#define int long long
using namespace std ;
int n , p , k ;
int a[ 110 ] ;
struct Matrix {
int a[ 110 ][ 110 ] ;
void clear () { memset ( a , 0 , sizeof ( a ) ) ; }
} ans , b ;
Matrix operator * ( Matrix A , Matrix B ) {
Matrix C ;
C.clear () ;
for ( int i = 1 ; i <= 100 ; i ++ )
for ( int j = 1 ; j <= 100 ; j ++ )
for ( int k = 1 ; k <= 100 ; k ++ )
C.a[ i ][ j ] = ( C.a[ i ][ j ] + A.a[ i ][ k ] * B.a[ k ][ j ] ) % p ;
return C ;
}
inline int qpow ( int a , int b ) {
int res = 1 ;
while ( b ) {
if ( b & 1 ) res = res * a % p ;
a = a * a % p ;
b >>= 1 ;
}
return res ;
}
inline void Matrix_pow ( int bs ) {
while ( bs ) {
if ( bs & 1 ) ans = ans * b ;
b = b * b ;
bs >>= 1 ;
}
}
signed main () {
ios :: sync_with_stdio ( false ) ;
cin >> n >> p >> k ;
b.clear () ;
for ( int i = 1 ; i <= k ; i ++ ) {
int t ; cin >> t ;
b.a[ t ][ 1 ] = 1 ;
}
for ( int i = 2 ; i <= 100 ; i ++ ) b.a[ i - 1 ][ i ] = 1 ;
ans.a[ 1 ][ 1 ] = 1 ;
Matrix_pow ( n ) ;
printf ( "%lld
" , ( qpow ( 2 , n - 1 ) - ans.a[ 1 ][ 1 ] + p ) % p ) ;
return 0 ;
}