思路:
要注意到添加 \(1\) 和删除 \(0\) 是等价的。
先令 \(0 \to -1\)。
首先猜了一个结论,先顺着走,做一个前缀和,若当且位置的前缀和 \(<0\),那么需要删除这个位置的 \(0\),使得前缀和为正;然后再反着做一遍,那么答案就是删除的 \(0\) 的个数。
暴力 Code:
int main(){
n=read(),q=read();
For(i,1,n){
a[i]=get()-'0';
if(!a[i])
a[i]=-1;
}
while(q--){
sum=ans=0;
l=read(),r=read();
For(i,l,r){
sum+=a[i];
if(sum<0){
sum++,ans++;
f[i]=1;
}
}
_For(i,l,r){
if(f[i]){
f[i]=0;
continue;
}
sum+=a[i];
if(sum<0)
sum++,ans++;
}
write(r-l+1-ans);
putchar('\n');
}
return 0;
}
考虑优化,令 \(pre_i\) 表示 \([l,i]\) 的前缀和,\(suf_i\) 表示 \([i,r]\) 的后缀和。
则正着扫的时候,若 \(pre_i=-1\),相当于将 \(j \in [i,r]\) 的 \(pre_j\) 都增加了 \(1\),继续扫的时候若碰到 \(pre'_k = -1\),经过前面的加法操作前 \(pre_k = -2\),此时也需要给 \(j \in [k,r]\) 的 \(pre_j\) 增加 \(1\)。
那么我们就可以发现,后面若第 \(i\) 次找到 \(pre'_j=-1\),则原来的 \(pre_j = -i\)。
同时我们注意到 \(pre_i\) 是由 \(pre_{i-1}\) 加减 \(1\) 变换而来的,则访问的值域是连续的,则对于第一次正着扫需要的删除次数为:
\[-\min\limits_{i=l}^r pre_i \]然后再考虑反着扫的贡献,同样也是找到 \(suf'_i = -1,-2,-3,\cdots\),但是这里 \(suf'\) 是 \(suf\) 经过正着扫后变化的。
在正着扫的时候,删除 \(i\) 处的 \(0\),相当于将 \(j \in [l,i]\) 的 \(suf_i\) 加 \(1\),即我们需要维护 \(i\) 这个位置右侧被删除的 \(0\) 的个数,因为正着扫是从左往右的,考虑做个差好维护一些,即相当于 \(-\min\limits_{i=l}^r pre_i\) 减去 \(i\) 左侧被删除的 \(0\) 的个数。
\(i\) 左侧被删除的 \(0\) 的个数,即 \(- \min\limits_{j=l}^i pre_j\),则我们得到了:
\[suf'_i = suf_i + (-\min\limits_{j=l}^r pre_j + \min\limits_{j=l}^i pre_j) = suf_i - \min\limits_{j=l}^r pre_j + \min\limits_{j=l}^i pre_j \]则反着扫的贡献是:
\[-\min\limits_{i=l}^r suf'_i = -\min\limits_{i=l}^r \Big( suf_i - \min\limits_{j=l}^r pre_j + \min\limits_{j=l}^i pre_j \Big) = -\min\limits_{i=l}^r \Big( suf_i + \min\limits_{j=l}^i pre_j \Big) + \min\limits_{j=l}^r pre_j \]则删除 \(0\) 的总数为:
\[\min\limits_{j=l}^r pre_j - \min\limits_{i=l}^r \Big( suf_i + \min\limits_{j=l}^i pre_j \Big) - \min\limits_{j=l}^r pre_j = - \min\limits_{i=l}^r \Big( suf_i + \min\limits_{j=l}^i pre_j \Big) \]考虑这个式子的抽象意义:求出一个区间内不相交的一个后缀和加上一个前缀和的最小值,容斥一下,转化为区间和减去中间的一段,即我们要使得中间的一段最大,即区间最大子段和问题,使用线段树维护即可。
时间复杂度为 \(O(N \log N)\)。
完整代码:
#include<bits/stdc++.h>
#define Add(x,y) (x+y>=mod)?(x+y-mod):(x+y)
#define lowbit(x) x&(-x)
#define pi pair<ll,ll>
#define pii pair<ll,pair<ll,ll>>
#define iip pair<pair<ll,ll>,ll>
#define ppii pair<pair<ll,ll>,pair<ll,ll>>
#define fi first
#define se second
#define full(l,r,x) for(auto it=l;it!=r;it++) (*it)=x
#define Full(a) memset(a,0,sizeof(a))
#define open(s1,s2) freopen(s1,"r",stdin),freopen(s2,"w",stdout);
#define For(i,l,r) for(int i=l;i<=r;i++)
#define _For(i,l,r) for(int i=r;i>=l;i--)
using namespace std;
typedef long double lb;
typedef double db;
typedef unsigned long long ull;
typedef long long ll;
bool Begin;
const ll N=1e6+10;
inline ll read(){
ll x=0,f=1;
char c=getchar();
while(c<'0'||c>'9'){
if(c=='-')
f=-1;
c=getchar();
}
while(c>='0'&&c<='9'){
x=(x<<1)+(x<<3)+(c^48);
c=getchar();
}
return x*f;
}
inline void write(ll x){
if(x<0){
putchar('-');
x=-x;
}
if(x>9)
write(x/10);
putchar(x%10+'0');
}
inline char get(){
char c;
while(1){
c=getchar();
if(c=='0'||c=='1')
break;
}
return c;
}
ll n,q,l,r,t,ans;
ll a[N],s[N];
class Tree{
public:
struct Node{
ll l,r;
ll L,R;
ll sum;
ll data;
}X[N<<2];
Node pushup(Node A,Node B){
Node Ans;
Ans.l=A.l,Ans.r=B.r;
Ans.sum=A.sum+B.sum;
Ans.L=max(A.L,A.sum+B.L);
Ans.R=max(B.R,B.sum+A.R);
Ans.data=max({A.data,B.data,A.R+B.L});
return Ans;
}
void build(ll k,ll l,ll r){
X[k].l=l,X[k].r=r;
if(l==r){
X[k].sum=a[l];
X[k].data=X[k].L=X[k].R=max(a[l],0ll);
return ;
}
ll mid=(l+r)>>1;
build(k<<1,l,mid);
build(k<<1|1,mid+1,r);
X[k]=pushup(X[k<<1],X[k<<1|1]);
}
Node query(ll k,ll l,ll r){
if(X[k].l==l&&r==X[k].r)
return X[k];
ll mid=(X[k].l+X[k].r)>>1;
if(r<=mid)
return query(k<<1,l,r);
else if(l>mid)
return query(k<<1|1,l,r);
else
return pushup(query(k<<1,l,mid),query(k<<1|1,mid+1,r));
}
}T;
bool f[N];
int main(){
// open("A.in","A.out");
n=read(),q=read();
For(i,1,n){
a[i]=get()-'0';
if(!a[i])
a[i]=-1;
s[i]=s[i-1]+a[i];
}
T.build(1,1,n);
while(q--){
l=read(),r=read();
t=(r-l+1)+(s[r]-s[l-1]-T.query(1,l,r).data);
if(!t)
puts("-1");
else{
write(t);
putchar('\n');
}
}
return 0;
}
标签:R4,pre,P8304,01,limits,min,suf,ll,define
From: https://www.cnblogs.com/rgw2010/p/18388033