P8304 [CoE R4 D] 01 串

rgw2010發表於2024-08-30

思路:

要注意到新增 \(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;
}

相關文章