837F - Prefix Sums
题意
定义函数 (p(x)) 会返回 (x) 这个数组的前缀和,定义 (A^i=p(A^{i-1}) (i > 0)),给出 (A^0) 求使得 (A^i) 中存在大于等于 (k) 的数的最小的 (i) 。
分析
打表容易发现当数的数量大于 (4) 时,直接暴力就很快。
关键就是数量有 (2) 或 (3) 个时,怎么快速计算答案。
模拟一下发现,存在递推关系:
即第 (0) 项矩阵为
[egin{bmatrix} a_1 & a_2 & a_3 end{bmatrix}
]
那么第一项的矩阵为
[egin{bmatrix} a_1 & a_2 & a_3 end{bmatrix} * egin{bmatrix} 1 & 1 & 1\ 0 & 1 & 1\ 0 & 0 & 1 end{bmatrix}=egin{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;
}