Description
有一个长度为 (n) 的非负整数序列 (a_1, a_2, dots, a_n)。每一步你可以从以下三种操作中选择一种执行:
- 选择一个区间 ([l, r]),将下标在这个区间里的所有数都减 1。
- 选择一个区间 ([l, r]),将下标在这个区间里且下标为奇数的所有数都减 1。
- 选择一个区间 ([l, r]),将下标在这个区间里且下标为偶数的所有数都减 1。
求最少需要多少步才能将序列中的所有数都变成 0。
(nle 10^5,a_ile 10^9)。
Solution
首先题目的三种操作可以转化为两种操作:
- 从某一点出发,尽可能向右延伸直到到达为 (0) 的位置,将经过的点全部减 (1)。
- 从某一点出发,依次将右侧与当前点奇偶性相同的点 (-1) ,直到到达为 (0) 的位置。
考虑从左到右,将所有数变为 (0),设当前正在考虑 (a_i),如果 (a_{i+1}) 与 (a_i) 都不为 (0),那么直接从 (i) 出发执行一操作一定比执行二操作优,于是我们一直执行一操作直到 (a_i) 为 (0) 或 (a_{i+1}) 为 (0);然后若 (a_i ot= 0),再用二操作将 (a_i) 减到 (0)。
由于前面的操作会对当前点造成影响,不妨设可能会影响到当前点的一操作有 (x) 种,二操作有 (y) 种。
若 (a_i>x+y) ,那么直接减去 (x+y)。否则 (x,y) 中有部分操作在这里就终止了,我们令 (k=x+y-a_i),然后先让 (x,y,a_i) 都减去 (k),然后看作给 (a_i) 增加了 (k) 次免费操作的机会,让它来决定是选一操作还是二操作,问题就迎刃而解了。但是 (x) 有可能小于 (k),那么 (k-x) 个二操作一定延伸不了,直接让 (y-=k-x,k=x) 即可。(y<k) 时同理。
最终复杂度 (mathcal O(n))。
Code
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+10;
typedef long long ll;
int n,T;
ll a[N],ans;
int main(){
scanf("%d",&T);
while(T--){
scanf("%d",&n);
for(int i=1;i<=n;++i) scanf("%lld",&a[i]);
ll x=0,y=0,z=0;ans=0;
for(int i=2;i<=n+1;++i){
int d=0;
if(a[i]<x+y){
d=x+y-a[i];
if(x<d) y-=d-x,d=x;
if(y<d) x-=d-y,d=y;
x-=d;y-=d;a[i]-=d;
}
a[i]-=x+y;
ll tmp=min(a[i],a[i-1]);
x+=tmp;
a[i-1]-=tmp;a[i]-=tmp;ans+=tmp;
tmp=a[i-1];
z+=tmp;ans+=tmp;a[i-1]=0;
ans-=d;a[i]+=d;
swap(y,z);
}
printf("%lld
",ans);
}
return 0;
}