CF1491C Pekora and Trampoline 题解

题目链接

  比赛时只想到了 \(\mathcal O(n^3)\) 的暴力做法,官方题解是 \(\mathcal O(n^2)\) ,并且是可以优化为 \(\mathcal O(n)\) 的(贪心+差分)🐝。

​  贪心是比较明显的,我们需要从左到右依次考虑每张床的贡献,因为第 \(i\) 张床只能由 \([1,i-1]\) 的床跳过来,Pekora从第一张床开始跳一定是最优的。我们从左到右分析,用 \(b_i\) 记录在 \(i\) 床在之前被跳过的次数,如果 \(b_i>S_i-1\) ,说明 \(S_i\) 在之前就已经降为 \(1\) 了,多跳的部分会转移到 \(b_{i+1}\);否则还需要在 \(i\) 床上跳 \(S_i-b_i-1\) 次。然后对于每张床我们只需要考虑它能直接跳到的地方:因为它要从 \(S_i\) 减到 \(1\) ,所以它会把 \([i+2,min(S_i+i,n)]\) 区间上的所有床都跳过一次,即这个区间上的 \(b\) 都增一,这里如果用差分的话就是 \(\mathcal O(1)\) 的区间修改复杂度了,当然用暴力扫一遍也是可以过这道题的。这里给出使用差分的代码,\(b\) 在代码中为差分数组

\(\mathrm{Code}:\)

#include<bits/stdc++.h>
using namespace std;
const int N = 1e4 + 6;
typedef long long ll;
int n,s[N],b[N];//注意b[i]为差分数组

int main(){
    int t;scanf("%d",&t);
    while(t--){
        scanf("%d",&n);
        ll ans = 0;
        for(int i = 1;i <= n;i++) scanf("%d", s + i), b[i] = 0;b[n + 1]=0;
        int las = 0;
        for(int i = 1;i <= n;i++){
            int now = b[i] + las;  //now表示是i床在[1,i-1]中被跳了几次,即上面分析中的b[i]
            las = now;
            if(now > s[i] - 1){
                b[i+1] += now - s[i] + 1;
                b[i+2] -= now - s[i] + 1;
                now = s[i] - 1;
            }
            ans += s[i] - now - 1;
            if(min(s[i] + i, n) >= i + 2){
                b[i+2] ++;                     //相当于区间[i+2,min(s[i]+i,n)]增一
                b[min(s[i] + i, n) + 1]--;
            }
        }
        cout<<ans<<endl;
    }
    return 0;
}
posted @ 2021-03-01 12:54  ailanxier  阅读(292)  评论(0编辑  收藏  举报