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