AtCoder Beginner Contest 258 Ex. Odd Steps(带限制的矩阵快速幂)

题目

给定前缀和值s和长为n(n<=1e5)的序列A,第i个数ai(1<=a1<=a2<=...<=an<=s<=1e18)

求满足以下条件的序列X的数量,答案对998244353取模

限制条件:

1. X序列中的每个元素都是正奇数

2. X序列中的数的和为s(s<=1e18)

3. 记X序列的前缀和序列为Y,Y中不包含给定序列a中的值

思路来源

Solution of ABC 258 Ex - Powerless233 的博客 - 洛谷博客

题解

感觉思路来源已经写的非常清楚了

感觉矩阵快速幂优化比较自然,

主要没有想到这个朴素的做法,

f[i]=f[i-1]+f[i-3]+...

当然也学到了后面的前缀和限制的trick

1. 朴素算法+前缀和优化+矩阵快速幂优化

2. 给出矩阵快速幂转移式,并且有初始向量f[0]=1,s[-1]=0,s[-2]=0(边界条件)

3. 有前缀和不能为ai的限制,所以在n次矩阵快速幂转移时,手动给当前f[i]的值置为0

代码

//#include <bits/stdc++.h>
#include<iostream>
#include<cstdio>
#include<vector>
#include<map>
using namespace std;
#define rep(i,a,b) for(int i=(a);i<=(b);++i)
#define per(i,a,b) for(int i=(a);i>=(b);--i)
typedef long long ll;
typedef double db;
typedef pair<int,int> P;
#define fi first
#define se second
#define pb push_back
#define dbg(x) cerr<<(#x)<<":"<<x<<" ";
#define dbg2(x) cerr<<(#x)<<":"<<x<<endl;
#define SZ(a) (int)(a.size())
#define sci(a) scanf("%d",&(a))
#define pt(a) printf("%d",a);
#define pte(a) printf("%d\n",a)
#define ptlle(a) printf("%lld\n",a)
#define debug(...) fprintf(stderr, __VA_ARGS__)
using namespace std;
using namespace std;
typedef long long ll;
const int N=1e5+10,mod=998244353;
struct mat {
    static const int MAXN=3;
    ll c[MAXN][MAXN];
    int m, n;
    mat(){
    	memset(c, 0, sizeof(c));
    	m=n=MAXN;
    }
    mat(int a, int b) : m(a), n(b) {
        memset(c, 0, sizeof(c));
    }
    void clear(){
		memset(c, 0, sizeof(c));
    }
    mat operator * (const mat& temp) {
        mat ans(m, temp.n);
        for (int i = 0; i < m; i ++)
            for (int j = 0; j < temp.n; j ++){
                for (int k = 0; k < n; k ++){
                    ans.c[i][j] += c[i][k] * temp.c[k][j];
                    ans.c[i][j]%=mod;
                }
            }
        return ans;
    }
    mat operator ^(ll n){
        mat M(*this),ans(M.m, M.m);
    	for (int i = 0; i < M.m; i ++)
        ans.c[i][i] = 1;
   		while (n > 0) {
            if (n & 1) ans = ans * M;
            M = M * M;
            n >>= 1;
    	}
        return ans;
	}
}b,ans;
ll n,s,a[N];
int main(){
    scanf("%lld%lld",&n,&s);
    b.c[0][0]=b.c[0][2]=1;
    b.c[1][0]=b.c[1][2]=1;
    b.c[2][1]=1;
    ans.c[0][0]=1;
    rep(i,1,n){
        scanf("%lld",&a[i]);
        ans=(b^(a[i]-a[i-1]))*ans;
        ans.c[0][0]=0;
    }
    ans=(b^(s-a[n]))*ans;
    printf("%lld\n",ans.c[0][0]);
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Code92007

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值