C. Pekora and Trampoline

题意:

有n个蹦床排成一列,第i个蹦床有弹力值s[i],在当前位置你可以往右跳到第i + s[i]个蹦床如果i + s[i] > n
那么这次连跳结束,而且每跳一次弹力值减一但最小减到1
问你最少需要多少次起跳(就是第一次跳而不经过其他跳到),使得所有的蹦床弹力值都变为1

分析:

只有从前面的蹦床跳到后面才使得后面的蹦床少跳!
那么我只需要统计i前面的(1~i-1)所有蹦床对它少跳贡献多少,显然s[i] 会对 i + 2 ~ i + s[i]的后面所有蹦床有贡献
所以就用差分+前缀和来分别标记该蹦床对后面的蹦床的贡献,以及查询该蹦床前面的蹦床对它的贡献
这里有个坑就是 如果别人弹力值已经变为1了或者前面的贡献总和使它变为1还有剩余的贡献,那么这个贡献会顺延到他的下一个蹦床

代码

#include<bits/stdc++.h>
using namespace std;
const int N = 5e3 + 5;
#define ll long long
int s[N],presum[N];

int main()
{
	int t,n;
	scanf("%d",&t);
	while(t--)
	{
		scanf("%d",&n);
		for(int i = 1;i <= n;i++)
		scanf("%d",&s[i]);

		memset(presum,0,sizeof presum);
		ll cnt = 0;
		for(int i = 1;i <= n;i++)
		{
			presum[i] += presum[i-1];
			int nd = s[i] - presum[i] - 1;
			cnt += max(nd,0);
			if(i + 2 <= n) presum[i + 2] += 1,presum[min(i + s[i] + 1,n + 1)] -= 1;
			if(nd < 0 && i + 1 <= n) presum[i+1] += -nd,presum[i + 2] -= -nd; 
			//上面这一行代码就是说如果别人弹力值已经变为1了或者前面的贡献总和使它变为1还有剩余的贡献,
			//那么这个贡献会顺延到他的下一个蹦床 
		}
		printf("%lld\n",cnt);
	}
	return 0;
}

  

 

posted @ 2021-03-11 14:09  ChunhaoMo  阅读(72)  评论(0编辑  收藏  举报