【题解】[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);
}


                
            
        
浙公网安备 33010602011771号