Loading

CodeForces-1491C Pekora and Trampoline 贪心,递推

CodeForces-1491C Pekora and Trampoline 贪心,递推

题意

一条直线上有\(n\)个点,每次可以选择一个起点,若该点为\(S_i\),则会跳到\(i + S_i\),(不超过N)且\(S_i --\)(不小于1)。

问最少的放置次数,让所有\(S_i = 1\)

\[1 \leq n \leq 5000\\ 1\leq S_i \leq 10^9 \]

分析

想到了贪心,但没想到怎么实现。

贪心:每次一定从最开始的不是1的点开始放。

这样做也就是每次把当前点从\(S_i\)变为\(1\)的过程,考虑两个方面:之前的点对当前点的影响;当前点对之后的点的影响。

\(t_i\)表示之前点对当前点的影响

显然若\(t_i >= S_i - 1\),那么当前点已经是\(1\)了,那么当前点对之后点的影响:只会影响\(i+1\),影响的大小是\(t_i - S_i + 1\)

\(t_i < S_i - 1\),那么当前点还要重置\(S_i - 1 - t_i\)次,当前点对之后点的影响:不影响\(i + 1\),影响\([i+2,min(n,i+S_i+1)]\)各一次。

这样,复杂度O(n^2)

代码

#include<bits/stdc++.h>
#define pii pair<ll,int>
#define eps 1e-7
#define equals(a,b) (fabs(a - b) < eps)
#define fi first
#define se second
using namespace std;
typedef long long ll;

const int maxn = 5e5 + 5;
const ll MOD = 1e9 + 7;

ll rd(){
	ll x = 0;
	int f = 1;
	char ch = getchar();
	while(ch < '0' || ch > '9'){
		if(ch == '-')  f = -1;
		ch = getchar();
	}
	while(ch >= '0' && ch <= '9'){
		x = x * 10 + ch - '0';
		ch = getchar();
	}
	return x * f;
}

void solve(){
	int n = rd();
	vector<int> v(n + 1);
	vector<ll> cur(n + 1,0);
	for(int i = 0;i < n;i++)
		v[i] = rd();
	ll ans = 0;
	for(int i = 0;i < n;i++){
		ll tmp = cur[i];
		if(tmp < v[i] - 1) {
			ans += v[i] - 1 - tmp;
			tmp = v[i] - 1; 
		}
		cur[i + 1] += tmp - v[i] + 1;
		for(int j = i + 2;j < min(n,i + v[i] + 1);j++)
			cur[j]++;
	}
	cout << ans << '\n';
}

int main(){
	int T = rd();
	while(T--)
		solve();
}
posted @ 2021-03-02 10:18  MQFLLY  阅读(144)  评论(0编辑  收藏  举报