Codeforces 837F - Prefix Sums

837F - Prefix Sums

题意

定义函数 \(p(x)\) 会返回 \(x\) 这个数组的前缀和,定义 \(A^i=p(A^{i-1}) \ (i > 0)\),给出 \(A^0\) 求使得 \(A^i\) 中存在大于等于 \(k\) 的数的最小的 \(i\)

分析

打表容易发现当数的数量大于 \(4\) 时,直接暴力就很快。

关键就是数量有 \(2\)\(3\) 个时,怎么快速计算答案。

模拟一下发现,存在递推关系:

即第 \(0\) 项矩阵为
\[\begin{bmatrix} a_1 & a_2 & a_3 \end{bmatrix}\]
那么第一项的矩阵为
\[\begin{bmatrix} a_1 & a_2 & a_3 \end{bmatrix} * \begin{bmatrix} 1 & 1 & 1\\ 0 & 1 & 1\\ 0 & 0 & 1 \end{bmatrix}=\begin{bmatrix} a_1 & a_1+a_2 & a_1+a_2+a_3 \end{bmatrix}\]

后面类似,又显然存在单调性,可以使用二分求解。

这道题比较坑的就是会超 \(long \ long\) ,用了 \(long \ double\) 才能过,这种做法坑点有点多。

code

#include<bits/stdc++.h>
using namespace std;
const int MAXN = 2e5 + 10;
const int N = 10;
typedef long long ll;
ll a[MAXN];
int n, cnt = 0;
ll K;
struct Matrix {
    long double mat[N][N];
    void init() {
        for(int i = 0; i < cnt; i++) {
            for(int j = 0; j < cnt; j++) {
                mat[i][j] = 0;
            }
        }
    }
};
Matrix operator * (Matrix A, Matrix B) {
    Matrix C;
    C.init();
    for(int i = 0; i < cnt; i++) {
        for(int j = 0; j < cnt; j++) {
            for(int k = 0; k < cnt; k++) {
                C.mat[i][j] += A.mat[i][k] * B.mat[k][j];
            }
        }
    }
    return C;
}
Matrix operator ^ (Matrix A, ll x) {
    Matrix B;
    B.init();
    for(int i = 0; i < cnt; i++) {
        for(int j = 0; j < cnt; j++) {
            if(i == j) B.mat[i][j] = 1;
        }
    }
    while(x) {
        if(x & 1) B = B * A;
        A = A * A;
        x >>= 1;
    }
    return B;
}
int judge(ll x) {
    Matrix A;
    A.init();
    for(int i = 0; i < cnt; i++) {
        for(int j = i; j < cnt; j++) {
            A.mat[i][j] = 1;
        }
    }
    A = A ^ x;
    Matrix B;
    B.init();
    for(int i = 0; i < cnt; i++) B.mat[0][i] = a[i + 1];
    B = B * A;
    for(int i = 0; i < cnt; i++) {
        if(B.mat[0][i] >= K) return 1;
    }
    return 0;
}
int main() {
    ios::sync_with_stdio(0);
    cin.tie(0);
    cin >> n >> K;
    for(int i = 0; i < n; i++) {
        ll x;
        cin >> x;
        if(!cnt && !x) continue;
        if(x >= 0) { a[++cnt] = x; }
        if(x >= K) {
            K = 0;
        }
    }
    ll ans = 0;
    if(K && cnt >= 4) {
        int flr = 1;
        while(1) {
            for(int i = 1; i <= cnt; i++) {
                a[i] += a[i - 1];
                if(a[i] >= K) {
                    ans = flr;
                    break;
                }
            }
            if(ans) break;
            flr++;
        }
    } else if(K) {
        ll l = 0, r = K;
        while(l < r) {
            ll mid = (l + r) / 2;
            if(judge(mid)) r = mid;
            else l = mid + 1;
        }
        ans = l;
    }
    cout << ans << endl;
    return 0;
}

转载于:https://www.cnblogs.com/ftae/p/7360444.html

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值