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; }