题解:P6631 [ZJOI2020] 序列
对 这篇题解 进行一个解释说明。而且其实我也有不会证明的部分。
我们称一操作是直线操作,二三操作时跳线操作。
首先想到贪心。考虑做一些操作把
证明:若不存在则意味着
那么比较好的想法是,先做一些操作把
但是我们要考虑递归时会有一些之前的操作传过来。假设我们枚举到
由于若已经处理了前面操作对
若
若
设
我们注意到,直线和跳线不重要,重要的是他们都是从
于是这个问题就可以递归处理了!时间复杂度
但其实我仍然有不懂的地方:
1.为什么直接贪心没有后效性;
2.为什么让
我只能感性理解,但是能想到这个贪心还是很厉害的。
可以根据代码理解贪心流程。
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N=1e5+10;
int n,a[N];
void work(){
cin>>n;
for(int i=1;i<=n;i++) cin>>a[i];
a[n+1]=0;
LL A=0,B=0,C=0,ans=0;
for(int i=2;i<=n+1;i++){
LL tag=0;
if(a[i]<A+B){
LL K=A+B-a[i];
if(K>B) A-=K-B,K=B;
if(K>A) B-=K-A,K=A;
a[i]=0;
A-=K,B-=K,tag=K;
}else a[i]-=A+B;
LL Min=min(a[i-1],a[i]);
a[i]-=Min,a[i-1]-=Min,ans+=Min,A+=Min;
C+=a[i-1],ans+=a[i-1],a[i-1]-=a[i-1];
a[i]+=tag,ans-=tag;
swap(B,C);
}
cout<<ans<<'\n';
}
int main(){
ios::sync_with_stdio(false);
std::cin.tie(0);
std::cout.tie(0);
// freopen("b.in","r",stdin);
// freopen("b.out","w",stdout);
int T=1;
cin>>T;
while(T--){
work();
}
cerr<<1.0*clock()/CLOCKS_PER_SEC<<'\n';
return 0;
}