Codeforeces #420 E. Okabe and El Psy Kongroo 递推加矩阵快速幂

本文介绍了一种使用矩阵快速幂解决特定类型递推问题的方法。通过构造特定矩阵并利用快速幂技巧,可以在较短时间内计算出长序列的递推结果。文章提供了一个具体的编程实现案例。

比较好的一道题,首先是是递推,递推很容易想到,我们假设dp[i][j]表示走到i,j的方案数 那么其实就是求出每一段a[i]-b[i]的值 就可以得出来了

可以得到

dp[i][j+1] += dp[i-1][j]; j+1<=ci

dp[i][j-1] += dp[i-1][j];  j-1>=0

dp[i][j] += dp[i-1][j];

因为i的范围很大,看了解法后知道要用矩阵快速幂

我们构造一个16*16的矩阵 矩阵mat[i][j] 表示从y轴的i点走到y轴的j点的方案数,当长度为一的时候这个矩阵很好构造 那么长度为2的时候其实就是mat^2,那么直接快速幂求出长度为b[i]-a[i],然后每一段的方案知道后这个题就基本解决了 详细看代码

#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<string>
#include<set>
#include<map>
#include<queue>
#include<stack>
#include<cstring>

using namespace std;
#define LL long long
#define N 18
#define MOD 1000000007ll
struct Matrix{
    LL dat[N][N];
    int n;
    Matrix(){
        _init();
    }
    void _init(){
        memset(dat,0,sizeof(dat));
    }
    LL* const operator[](int i)
    {
		return dat[i];
    }
	const LL*  operator[](int i)const
	{
		return dat[i];
	}
    void operator%(LL mod)
    {
        for(int i = 0;i<n;i++)
        {
            for(int j = 0;j<n;j++)
            {
                dat[i][j]%=mod;
            }
        }
    }
    Matrix operator *(const Matrix& mat){
        Matrix temp;
        temp.n = mat.n;
        for(int i = 0;i<n;i++)
        {
            for(int j = 0;j<n;j++)
            {
                for(int k = 0;k<n;k++)
                {
                   temp[i][j] += dat[i][k]*mat[k][j];
                   temp[i][j]%=1000000007ll;
                }
            }
        }
        return temp;
    }
};
Matrix ans,sta;
void quickPow(LL len)
{
    while(len)
    {
        if(len&1)
        {
            ans = ans*sta;
            ans%MOD;
        }
        len >>= 1;
        sta = sta*sta;
        sta%MOD;
    }
}
int main()
{
    LL n,k;
    while(scanf("%lld%lld",&n,&k)!=EOF)
    {
        LL a[105],b[105],c[105];
        for(int i = 0;i<n;i++)
        {
            scanf("%lld%lld%lld",&a[i],&b[i],&c[i]);
        }
        ans._init();
        ans.n = 16;
        sta.n = 16;
        for(int i = 0;i<=ans.n;i++)
        {
            ans[i][i] = 1;
        }
        sta._init();

        for(int i = 0;i<n;i++)
        {
            sta._init();
            for(int j = 0;j<=c[i];j++)
            {
                if(j+1<=c[i])
                    sta[j][j+1]++;
                if(j-1>=0)
                    sta[j][j-1]++;
                sta[j][j]++;
            }
            if(b[i]>k)
            {
                quickPow(k-a[i]);
            }
            else quickPow(b[i]-a[i]);
        }

        printf("%lld\n",ans[0][0]);
    }
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值