题目传送门
题意:
有一个长度为n的字符串,每个字符是0或1,初始状态为全1。有一种操作,你可以把连续m个1变为0。你可以不进行操作,或进行任意次操作。问你可以产生多少种字符串。
数据范围:1 <= n <= 1e18,2 <= m <= 100。
题解:
定义f[i]表示前i个字符最多形成多少种形态。
构造递推式,f[i] = f[i - 1] + f[i - m]。
因为n比较大,考虑矩阵加速。f[n]即为答案。
构造的初始矩阵和转移矩阵在代码init函数中体现。
主要是我不会画图,这里就不写了,不过想到矩阵加速后,就很好构造了。
感受:
这道题使我收集了矩阵快速幂和矩阵加速的模板,good。
同时,在ans * res时可以优化,因为ans只需要第1行,所以可以只计算第1行的结果,常数减少了一倍。
代码:
#include<bits/stdc++.h>
using namespace std ;
typedef long long ll ;
const int maxn = 105 ;
const ll mod = 1e9 + 7 ;
int n ;
struct mat
{
ll m[maxn][maxn] ;
} ans , base ;
void init(int m)
{
n = m ;
memset(ans.m , 0, sizeof(ans.m)) ;
ans.m[1][1] = 2 ;
for(int i = 2 ; i <= n ; i ++) ans.m[1][i] = 1 ;
memset(base.m , 0 , sizeof(base.m)) ;
base.m[1][1] = base.m[n][1] = 1 ;
for(int i = 2 ; i <= n ; i ++) base.m[i - 1][i] = 1 ;
}
mat mul_ans(mat a , mat b)
{
mat res ;
memset(res.m , 0 , sizeof(res.m)) ;
for(int i = 1 ; i <= 1 ; i ++)
for(int j = 1 ; j <= n ; j ++)
for(int k = 1 ; k <= n ; k ++)
{
res.m[i][j] += (a.m[i][k] % mod) * (b.m[k][j] % mod) ;
res.m[i][j] %= mod ;
}
return res ;
}
mat mul(mat a , mat b)
{
mat res ;
memset(res.m , 0 , sizeof(res.m)) ;
for(int i = 1 ; i <= n ; i ++)
for(int j = 1 ; j <= n ; j ++)
for(int k = 1 ; k <= n ; k ++)
{
res.m[i][j] += (a.m[i][k] % mod) * (b.m[k][j] % mod) ;
res.m[i][j] %= mod ;
}
return res ;
}
void mat_qpow(ll p)
{
while(p)
{
if(p & 1) ans = mul_ans(ans , base) ;
base = mul(base , base) ;
p >>= 1 ;
}
}
int main()
{
ll n ;
int m ;
scanf("%lld%d" , &n , &m) ;
if(n < m) printf("1\n") ;
else if(n == m) printf("2\n") ;
else init(m) , mat_qpow(n - m) , printf("%lld\n", ans.m[1][1]) ;
return 0 ;
}