OnlyPersistVictory DaySix

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 1T500,1n5000,1a[i]109

题解:
首先显然的是,一定是优先选择前面的位置,这样一次可以使得多个 a i a_i ai减一。

i i i被选择作为起点时,一定是 a [ 1 ] , . . . , a [ i − 1 ] a[1],...,a[i-1] a[1],...,a[i1]均为 1 1 1了。
考虑 i i i作为起点的次数,是其 a [ i ] a[i] a[i]减去 i i i [ 1 , i − 1 ] [1,i-1] [1,i1]作为起点的路径经过的次数,这里令 i i i [ 1 , i − 1 ] [1,i-1] [1,i1]作为起点的路径经过的次数为 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;
}

猜你喜欢

转载自blog.csdn.net/weixin_43900869/article/details/115458840