题解:P8304 [CoE R4 D] 01 串
前言
本文同步自洛谷专栏,题目传送门。
前置练习:小白逛公园。
参考资料:另一篇题解,作者 juruo999 大佬。
写作原因:看参考资料的时候,由于太弱了,公式推导有点迷糊。
解题
问题转化
题目中给了前缀和后缀的限制,考虑转化一下:将 \(0\) 视作 \(-1\)。
那么限制就可以直接利用前缀和、后缀和表述:任意前缀和非负,任意后缀和非负。
又由于求子序列长度最大值,所以 \(1\) 都要保留,现在需要求解至少删去多少个 \(-1\) 才能满足要求,设当前询问区间 \([L,R]\) 删去的个数为 \(ans\),我们需要最小化它。
下面的分析将基于询问区间为 \([L,R]\)。
对于一个前缀 \([L,i]\),如果和(记作 \(pre_i\))为负数,那么在这个前缀中至少要删去 \(-pre_i\) 个 \(-1\);
对于后缀同理,记 \([i,R]\) 的和为 \(suf_i\)。
令 \(pre_{L-1}=0,suf_{R+1}=0\)。
具体分析
首先分析 \(ans\) 的下界:对于一组下标 \(i,j(L\le i<j\le R)\),必须保证 \([L,i],[j,R]\) 合法,
那么就有下界:
注意:边界的扩展是为了处理类似于 \(\forall L\le i\le R,pre_i>0\) 的情况,但由于这种情况在统计答案时可以方便处理,下文将使用 \([L,R]\) 区间。
尝试考虑能否取到这个下界。
首先保证前缀合法,那么在这一步中,对于一个前缀 \([L,i]\),其中删去的 \(-1\) 的个数是 \(\max_{L\le j\le i}\{-pre_j\}\),
\([L,R]\) 区间就删去了 \(\max_{L\le j\le R}\{-pre_j\}\) 个。
下面考虑保证后缀合法。
保证前缀合法的过程中,对后缀也有影响,设 \(suf_i\) 可变成的最大值为 \(h_i\)。
那么,对于能在 \(i\) 右边删的 \(-1\),就尽量不要放在左边,则 \(h_i\) 和 \(suf_i\) 的差值为 \([L,R]\) 中至少要删的数减去 \([L,i)\) 中至少要删的数。即:
在这一步中要删的数的个数为 \(\max_{L\le i\le R}\{-h_i\}\)。
经过这两步,所有前缀后缀都合法,则在此方案下,至少要删的数的个数为:
可以消掉两项,并且把里面的 \(\max\) 与外面的合并:
恰好等于下界,故可以取到。
实现
考虑如何快速求出该值。令区间 \([l,r]\) 的和为 \(sum_{l,r}\),区间不合法则 \(sum_{l,r}=0\)。
\(\max\) 中的形式为最大子段和与 \(0\) 的较大值,求解方式为线段树,具体的见前置练习,
由于 \(ans\) 描述的是删数的个数的最小值,那么最多可以保留的数的个数:
复杂度
同前置练习复杂度,时间 \(O(n\log{n})\),空间 \(O(n)\)。
代码
注意特判没有合法子序列的情况。
$\red{\text{code}}$
#include<bits/stdc++.h>
using namespace std;
#define N 500005
#define mid ((l+r)>>1)
struct node{
int pre,suf,sum,maxn;
}tr[N<<2],ret;
char s[N];
inline int read(){
int a=0;char ch=getchar();
while(!isdigit(ch)) ch=getchar();
while(isdigit(ch)) a=10*a+ch-'0',ch=getchar();
return a;
}
void merge(node &a,node &b,node &c){
a.sum=b.sum+c.sum;
a.pre=max(b.pre,b.sum+c.pre);
a.suf=max(c.suf,c.sum+b.suf);
a.maxn=max(b.maxn,c.maxn);
a.maxn=max(a.maxn,b.suf+c.pre);
}
void build(int p,int l,int r){
if(l==r){
if(s[l]=='0') tr[p]={-1,-1,-1,-1};
else tr[p]={1,1,1,1};
return;
}
build(p<<1,l,mid),build(p<<1|1,mid+1,r);
merge(tr[p],tr[p<<1],tr[p<<1|1]);
}
void query(int p,int l,int r,int ql,int qr,node &x){
if(ql<=l&&r<=qr) return x=tr[p],void();
if(ql>mid) return query(p<<1|1,mid+1,r,ql,qr,x),void();
if(mid>=qr) return query(p<<1,l,mid,ql,qr,x),void();
node y,z;
query(p<<1,l,mid,ql,qr,y),query(p<<1|1,mid+1,r,ql,qr,z);
merge(x,y,z);
}
int main(){
int n=read(),q=read();
scanf(" %s",s+1),build(1,1,n);
for(int i=1;i<=q;i++){
int l=read(),r=read(),res;
query(1,1,n,l,r,ret);
res=r-l+1+ret.sum-max(0,ret.maxn);
if(res==0) puts("-1");
else printf("%d\n",res);
}
return 0;
}

浙公网安备 33010602011771号