P8304 [CoE R4 D] 01 串

思路:

要注意到添加 \(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;
}
posted @ 2024-08-30 10:00  rgw2010  阅读(46)  评论(0编辑  收藏  举报