F
题意:
T T T组数据,给定一个序列 a a a,当你当前在位置 i i i时,会跳到 i + a [ i ] i +a[i] i+a[i],并且使得 a i a_i ai减 1 1 1。当你跳到一个大于 n n n的位置时,将停止继续跳跃。问最多跳多少轮回使得所有 a [ i ] a[i] a[i]变成 1 1 1。
数据范围: 1 ≤ T ≤ 500 , 1 ≤ n ≤ 5000 , 1 ≤ a [ i ] ≤ 1 0 9 1\leq T\leq 500,1\leq n\leq 5000,1\leq a[i]\leq 10^9 1≤T≤500,1≤n≤5000,1≤a[i]≤109
题解:
首先显然的是,一定是优先选择前面的位置,这样一次可以使得多个 a i a_i ai减一。
当 i i i被选择作为起点时,一定是 a [ 1 ] , . . . , a [ i − 1 ] a[1],...,a[i-1] a[1],...,a[i−1]均为 1 1 1了。
考虑 i i i作为起点的次数,是其 a [ i ] a[i] a[i]减去 i i i被 [ 1 , i − 1 ] [1,i-1] [1,i−1]作为起点的路径经过的次数,这里令 i i i被 [ 1 , i − 1 ] [1,i-1] [1,i−1]作为起点的路径经过的次数为 c n t [ i ] cnt[i] cnt[i]。
那么实际选择时,位置 i i i可以作为起点的条件其实是 a [ i ] − c n t [ i ] > 1 a[i]-cnt[i]>1 a[i]−cnt[i]>1。
具体操作:
-
点 i i i作为起点的次数, b [ i ] = a [ i ] − c n t [ i ] > 1 b[i]=a[i]-cnt[i]>1 b[i]=a[i]−cnt[i]>1,那么将会对答案贡献 b [ i ] − 1 b[i]-1 b[i]−1
-
考虑每个点之后的点,至少会使得 [ i + 2 , m i n ( i + 2 , i + a [ i ] ) ] [i+2,min(i+2,i+a[i])] [i+2,min(i+2,i+a[i])]的点 c n t cnt cnt都加 1 1 1
-
考虑一个点当前的 b [ i ] = a [ i ] − c n t [ i ] ≤ 1 b[i]=a[i]-cnt[i]\leq1 b[i]=a[i]−cnt[i]≤1,且当前跳到了该带你,那么其会使得 c n t [ i + 1 ] cnt[i+1] cnt[i+1]加 1 1 1,那么具体的次数为: c n t [ i ] − a [ i ] + 1 cnt[i]-a[i]+1 cnt[i]−a[i]+1
代码:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 5010;
ll cnt[N], res;
int a[N], n;
void solve() {
scanf("%d", &n);
for(int i = 1; i <= n; ++i)
scanf("%d", &a[i]), cnt[i] = 0;
res = 0;
for(int i = 1; i <= n; ++i) {
res += max(0ll, a[i] - cnt[i] - 1);
for(int j = i + 2; j <= min(n, i + a[i]); ++j) cnt[j] += 1;
cnt[i + 1] += max(0ll, cnt[i] - a[i] + 1);
}
printf("%lld\n", res);
}
int main()
{
int T = 1;
scanf("%d", &T);
for(int i = 1; i <= T; ++i) solve();
return 0;
}