Codeforces Global Round 13 C. Pekora and Trampoline

Codeforces Global Round 13 C. Pekora and Trampoline

原题传送门

题目大意

告诉你公园里有n个蹦床,每个蹦床有一个能量值Si, 当你跳上第i个蹦床后,你会跳向第i+Si 个蹦床,并且Si 会减1如果它大于1的话,但是如果i+Si大于n,那么就会停止这趟奇妙的旅行,否则,他将继续按照这个规则跳下去,直到停止,然后题目问你的是求将所有的Si 变成1所需要的最少的旅行次数

分析:这题因为数据较小,所以O(n2 )也能过,这里给出一个O(n)的解法,不是dsu,那个我不会,具体用差分来做。

1.首先我们来贪心♂的考虑这件事情。注意到我们是为了把所有的Si 变成1,对于已经变成1的如果我们跳上去,只会一个一个的向右侧移动,直到遇到第一个不为1的Si 。我们来考虑如果跳上一个蹦床对整体产生的贡献,显然,它会让本身的Si 减1,并且让它路径上所有的Sj>1的点减1;这些点一定在它的右侧,所以Si 对整体产生的贡献一定都在该点本身以及向右的位置,如果跳出界的话就只有对本身的贡献。这样我们就很清楚的知道,如果一个Si >1并且在i的前面不存在点的能量值大于1,则对该点的减小操作有且仅能从该点出发,执行Si -1次操作。

2.我们知道,一旦我们跳上一个蹦床,它后面的路径就已经被确定了,换句话说,一个路径可以由该路径的起始点来表示。这个很显然。接着我们再继续1的思考。对于第一个Si >1的i来讲,这Si -1次操作会让它从自身出发跳到的点的位置逐渐偏左。第一次从Si 出发跳到的第一个点是Si +i,第二次则是Si +i-1…最后一次便是Si +2。如果他的Si 已经为1就没必要继续操作了,所以最后一次一定是在Si +2。那么该点所需要的最小操作次数为Si -1次。

3.那么问题来了,我们要如何记录这些信息,又要如何处理呢。如果我们按照上述操作,第一个Si 处理完毕后,如果在这个Si 后面还有大于1的点(从Si 出发的路径对整体的贡献先咕咕咕),我们就要分析从Si 出发的所有路径中是否有点被路径覆盖,如果没有,我们就继续1,2的操作,如果有,我们就必须记录并处理这些信息。我们在2中已经分析过,一个路径可以由一个起始点来表示,那么Si 产生的路径便是从Si+2一直到Si +i-1。我们给这些点打上一个标记,考虑到是区间,所以我们很自然的就能想到利用差分数组,我们维护一个差分数组d,两端打上标记,如果越界就打到n+1。

这样我们在从左往右遍历点的时候,利用差分求前缀和,就知道目前点白嫖的旅行有多少次。如果目前白嫖的旅行次数大于等于了Sj ,那么这个点一定已经变成了1,对于溢出的部分就让它向右走,因为它只能跳一格了,它已经跳不动了。这样给后面两个点再打上差分标记。

但是如果白嫖的不够厉害,我自己还要付出钱去旅行(即这时候又回到1中的情况),重复上述操作即可。但是可能会有人会问,如果我只是给每个点扩撒得第一个点打上标记,我又不知道他之后能跳几格,毕竟是在后面得先跳,可能会跳到重复的点。不,这没有任何关系,我们从左往右遍历的时候,一定能知道当前点的白嫖次数,这就足够求出,它本身能跳到的下一个的点的集合,因为Sj只能一点一点的变小,每次只能减1,即使后面的点先跳,我们只要知道那个点到底白嫖了几次,就足以求出每个点必要的旅行费用。

如果我们直接分析一个点,假设它前面的点都已经处理完毕了,那么当前的点的被前面所覆盖的次数,便是白嫖次数,对于当前点的每一次旅行,它的下一个点总是确定的,所以我们只需要搜集一个左侧的信息,就一定能推出当前点的信息,从而推至右侧。

萌新(没有萌新实力的菜鸡)第一次发题解,写的逻辑不是很好,求各位大佬照顾,轻喷,主要是为了巩固而用的。

以下是代码

#include <iostream>
#include<stdio.h>
#include<algorithm>

using namespace std;
typedef long long ll;
const int N = 1e5+10;
ll t,n,m,q,u,v;
ll s[N],d[N];
void solve(){
    cin>>n;
    for(int i = 1;i <= n;++i){cin>>s[i];d[i] = 0;}
    ll ans = 0;
    for(int i = 1;i <= n;++i){
        d[i] += d[i-1];
        ++d[i+2],--d[min(n,s[i]+i)+1];
        if(d[i] >= s[i])d[i+1] += d[i] - s[i] + 1, d[i+2] -= d[i] - s[i] + 1;
        s[i] = max(s[i]-d[i],1ll);
        ans += s[i] - 1;
    }
    cout<<ans<<endl;
}

int main() {
    freopen("in.txt","r",stdin);
    freopen("out.txt","w",stdout);
    cin>>t;

    while(t--){
        solve();
    }

    fclose(stdin);
    fclose(stdout);
    return 0;
}

猜你喜欢

转载自blog.csdn.net/yfy20020106/article/details/114261594
今日推荐