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 ;
}
发布了215 篇原创文章 · 获赞 12 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/Irving0323/article/details/104086902
今日推荐