Pekora and Trampoline

Pekora and Trampoline

给出\(n\)个数,每次选择一个起点开始,然后可以从\(i\)的位置移动到\(i+s_i\),并且会使得\(s_i\)减1,除非\(s_i=1\),不断进行这个过程直到超出\(n\),求解最少经过多少次能够将所有数变成1. \(n\le5000,s_i\le10^9\)

然后考虑每个数被经过之后就会依次到达\(i+s_i...i+1\)这些位置,然后继续弹跳,那么后面的状态与前面怎样完全无关,这是本题的关键性质。所以我们应该每次贪心从第一个不是1的位置开始向后移动,因为这些数必然要被走,那么先走必然不劣,所以这样得到的必然是最优解

那么我们就可以根据这个东西来进行贪心,维护每个位置在之前已经被经过的次数,然后分类向后更新即可。

#include<bits/stdc++.h>
#define LL long long
#define V inline void
#define I inline int
#define FOR(i,a,b) for(register int i=a,end##i=b;i<=end##i;++i)
#define REP(i,a,b) for(register int i=a,end##i=b;i>=end##i;--i)
#define go(i,x) for(int i=hed[x];i;i=e[i].pre)
using namespace std;
inline int read()
{
	char x='\0';
	int fh=1,sum=0;
	for(x=getchar();x<'0'||x>'9';x=getchar())if(x=='-')fh=-1;
	for(;x>='0'&&x<='9';x=getchar())sum=sum*10+x-'0';
	return fh*sum;
}
const int N=5009;
int T,n,a[N],cur[N];
int main()
{
	T=read();
	while(T--)
	{
		LL ans=0;
		n=read();
		FOR(i,1,n)a[i]=read(),cur[i]=0;
		FOR(i,1,n)
		{
//			cout<<i<<" "<<cur[i]<<endl;//
			int tmp=cur[i];
			if(cur[i]<a[i]-1)
			{
				ans+=a[i]-1-cur[i];
				tmp=a[i]-1;
			}
			cur[i+1]+=tmp-a[i]+1;
//			cout<<min(n,i+a[i])<<endl;//
			FOR(j,i+2,min(n,i+a[i]))cur[j]++;
		}
		printf("%lld\n",ans);
	}

	return 0;
}


 
posted @ 2021-03-09 19:17  dinlon  阅读(53)  评论(0)    收藏  举报