codeforces1117D 2000分矩阵加速

题目传送门

题意:

有一个长度为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 ;
}

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值