专栏文章
题解:P6631 [ZJOI2020] 序列
P6631题解参与者 1已保存评论 0
文章操作
快速查看文章及其快照的属性,并进行相关操作。
- 当前评论
- 0 条
- 当前快照
- 1 份
- 快照标识符
- @min2u84o
- 此快照首次捕获于
- 2025/12/01 19:39 3 个月前
- 此快照最后确认于
- 2025/12/01 19:39 3 个月前
对 这篇题解 进行一个解释说明。而且其实我也有不会证明的部分。
我们称一操作是直线操作,二三操作时跳线操作。
首先想到贪心。考虑做一些操作把 变成 再递归处理。此时我们能发现,若 且 ,那么一定有一个过 的直线操作。
证明:若不存在则意味着 会做一次跳线操作, 会做一次跳线操作,可以把这两次操作合并,效果不变。
那么比较好的想法是,先做一些操作把 变成 ,然后若 ,再补充一些跳线操作。
但是我们要考虑递归时会有一些之前的操作传过来。假设我们枚举到 ,设 及以后都是 ,我们要把 变成 。此时有 个 的直线操作, 个 的跳线操作, 个 的跳线操作。
由于若已经处理了前面操作对 的影响后就可以按照上述做法直接做了,所以我们只考虑前面对 的影响。
若 ,那么我们贪心的把所有跳线和直线都保留,。
若 ,我们还是贪心的把 ,但是保留直线操作还是跳线操作呢?
设 ,表示我们要删掉 个直线或跳线操作。若 ,意味着至少要删 个跳线,那么直接删, 同理。所以我们钦定 。
我们注意到,直线和跳线不重要,重要的是他们都是从 开始的,我们不妨认为直线和跳线都删了 个,最后再反悔 次操作,怎么反悔呢?我们 ,那么当 的时候,我就必须要选择恰好 次操作把 干掉,并且我们不关心反悔的是直线还是跳线。
于是这个问题就可以递归处理了!时间复杂度 。
但其实我仍然有不懂的地方:
1.为什么直接贪心没有后效性;
2.为什么让 尽可能的小会更优。
我只能感性理解,但是能想到这个贪心还是很厉害的。
可以根据代码理解贪心流程。
CPP#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;
}
相关推荐
评论
共 0 条评论,欢迎与作者交流。
正在加载评论...