【题解】[Codeforces 1400E] Clear the Multiset

题目链接

题意

有一序列 \(a\),每一步你可以做以下两种操作之一:

  • \(a[l..r]\) 减一;
  • \(a[i]\)\(x\)
  • (你需要保证操作后数字非负)

问最少多少步将整个序列变成 \(0\)\(n\leq 5\times 10^3\)(实际可以做到 \(O(n\log n)\) 甚至 \(O(n)\))。

题解

不会出现左图的情况,左图一定能化为右图且答案不会更劣。

故递归处理:对于一段,找到其最小值,这一段有两种策略:

  • 这一段的最小值部分用第一种操作,接着序列被 \(0\) 分成多个部分,每个部分递归找最优解;
  • 这一段全部用第二种操作。

暴力是 \(O(n^2)\) 的,用笛卡尔树容易做到 \(O(n)\)(没写)。

#include<bits/stdc++.h>
using namespace std;
int getint(){
    int ans=0,f=1;
    char c=getchar();
    while(c<'0'||c>'9'){
        if(c=='-')f=-1;
        c=getchar();
    }
    while(c>='0'&&c<='9'){
        ans=ans*10+c-'0';
        c=getchar();
    }
    return ans*f;
}
const int N=5e3+10;
#define ll long long
int a[N];
ll solve(int l,int r){
    int mn=0x7f7f7f7f;
    for(int i=l;i<=r;i++)mn=min(mn,a[i]);
    ll ans=0;
    int t=l;
    for(int i=l;i<=r;i++){
        a[i]-=mn;
        if(a[i]==0){
            if(i!=t)ans+=solve(t,i-1);
            t=i+1;
        }
    }
    if(t!=r+1)ans+=solve(t,r);
    ans+=mn;
    ans=min(ans,r-l+1ll);
    // cerr<<"> "<<l<<" "<<r<<" "<<ans<<endl;
    return ans;
}
int main(){
    int n=getint();
    for(int i=1;i<=n;i++)a[i]=getint();
    cout<<solve(1,n);
}
posted @ 2020-10-28 09:39  破壁人五号  阅读(89)  评论(0编辑  收藏  举报