传送门: https://codeforces.ml/contest/1491/problem/C
题意
给一个数组a,可以从任意地方开始跳。假如在第i个开始跳,那么跳到下一个的位置是I+a[i],并且当前位置a[i]–,跳到超过n为止,这算一个操作数。问,当操作数为多少时,可以使数组全部变成1,求出最少的操作数。
思路
比
赛
的
时
候
知
道
是
贪
心
,
但
是
不
会
贪
,
只
能
暴
力
模
拟
n
3
,
n
虽
然
只
有
5000
,
但
也
会
T
。
比赛的时候知道是贪心,但是不会贪,只能暴力模拟n^3,n虽然只有5000,但也会T。
比赛的时候知道是贪心,但是不会贪,只能暴力模拟n3,n虽然只有5000,但也会T。
而
贪
心
的
话
是
n
2
,
复
杂
度
是
够
的
。
而贪心的话是n^2,复杂度是够的。
而贪心的话是n2,复杂度是够的。
考 虑 每 一 次 都 从 第 一 个 走 , 这 无 所 谓 , 如 果 前 面 全 是 1 , 那 么 会 直 到 第 一 个 不 为 1 的 位 置 开 始 跳 。 考虑每一次都从第一个走,这无所谓,如果前面全是1,那么会直到第一个不为1的位置开始跳。 考虑每一次都从第一个走,这无所谓,如果前面全是1,那么会直到第一个不为1的位置开始跳。
所
以
从
前
往
后
遍
历
,
考
虑
当
前
第
i
位
,
有
多
少
是
要
我
们
操
作
的
,
有
多
少
是
从
之
前
的
操
作
跳
过
来
的
所以从前往后遍历,考虑当前第i位,有多少是要我们操作的,有多少是从之前的操作跳过来的
所以从前往后遍历,考虑当前第i位,有多少是要我们操作的,有多少是从之前的操作跳过来的
所
以
这
些
不
对
答
案
做
贡
献
。
\red{所以这些不对答案做贡献。}
所以这些不对答案做贡献。
假
设
第
i
个
,
那
么
他
会
跳
到
[
i
+
2
,
i
+
a
[
i
]
]
,
这
些
个
位
置
都
可
以
通
过
i
跳
到
,
假设第i个,那么他会跳到[i+2,i+a[i]],这些个位置都可以通过i跳到,
假设第i个,那么他会跳到[i+2,i+a[i]],这些个位置都可以通过i跳到,
而
第
i
+
1
个
位
置
是
必
然
跳
到
的
,
并
且
第
i
个
位
置
可
以
从
之
前
操
作
中
跳
过
来
,
那
么
第
i
+
1
位
置
也
是
可
以
从
之
前
操
作
跳
过
来
。
而第i+1个位置是必然跳到的,并且第i个位置可以从之前操作中跳过来,那么第i+1位置也是可以从之前操作跳过来。
而第i+1个位置是必然跳到的,并且第i个位置可以从之前操作中跳过来,那么第i+1位置也是可以从之前操作跳过来。
所 以 需 要 一 个 数 组 b 来 记 录 , 第 i 个 位 置 有 多 少 是 从 前 面 位 置 跳 过 来 的 , 这 些 不 对 答 案 做 贡 献 。 所以需要一个数组b来记录,第i个位置有多少是从前面位置跳过来的,这些不对答案做贡献。 所以需要一个数组b来记录,第i个位置有多少是从前面位置跳过来的,这些不对答案做贡献。
Code
#include "bits/stdc++.h"
using namespace std;
typedef long long ll;
void solve() {
int _;
cin >> _;
while (_--) {
int n;
cin >> n;
ll a[n + 10], b[n + 10];
for(int i = 1;i <= n; i++) cin >> a[i];
mem(b, 0);
ll ans = 0;
for(int i = 1;i <= n; i++) {
ll temp = b[i];
if(a[i] > temp + 1) {
ans += a[i] - temp - 1;
temp += a[i] - temp - 1;
}
b[i + 1] += temp - a[i] + 1;
if(i + 2 <= n) for(int j = i + 2;j <= min(1ll* n, i + a[i]); j++) b[j]++;
}
cout << ans << endl;
}
}
signed main() {
solve();
}