【AtCoder Regular Contest 085 F】NRE

数据结构优化 dp。

题目大意:

给定 01 串 $b$ 和全 0 串 $a$,再给定 $m$ 个区间 $[l_i,r_i]$,可以从中选择任意一些区间。

若第 $i$ 个区间被选择,则会将 $a_{l_i..r_i}$ 都变成 1

求 $\sum\limits_{i=1}^n [a_i\neq b_i]$ 的最小值。

题解:

一个巧妙的思路。

其中 $\sum\limits_{i=1}^n[b_i=0]$ 是定值。因此我们只需要最小化 $\sum\limits_{i=1}^n[b_i=0]+\sum\limits_{i=1}^n [a_i=0][b_i=1]-\sum_{i=1}^n[a_i=0][b_i=0]$ 的值即可。

这样一来,我们将区间赋 $1$ 的操作对答案的贡献就是 $0$,就没有复杂的讨论了。

考虑令 $f_i$ 表示到目前为止,最后一个 $1$ 在位置 $i$ 时的最小值。

假设当前考虑到位置 $x$:

  • 位置 $x$ 空着,那么对于 $i\in[0,x)$,$f_i=f_i+(2\cdot [b_i=1]-1)$。
  • 有线段的开头是位置 $x$,设右端点为 $r$,则 $f_r=\min\{f_r,f_0,f_1,\ldots,f_{r-1}\}]$。从 $0\sim r-1$ 更新 $r$ 就解决了线段重合的问题。

这个 dp 可以用线段树维护。

时间复杂度 $O(n\log n)$。

Code:

#include<iostream>
#include<algorithm>
#include<vector>
using namespace std;
const int N=2e5+5;
int n,b[N],m,sum;
vector<int>vec[N];
int d[N<<2],tag[N<<2];
void build(int l,int r,int o){
    if(l==r)d[o]=0x3fffffff;else{
        const int mid=l+r>>1;
        build(l,mid,o<<1),build(mid+1,r,o<<1|1);
        d[o]=0x3fffffff;
    }
}
void pushdown(int o){
    int&x=tag[o];
    tag[o<<1]+=x,tag[o<<1|1]+=x;
    d[o<<1]+=x,d[o<<1|1]+=x,x=0;
}
void modify(int l,int r,int o,const int&pos,const int&val){
    if(l==r)d[o]=min(d[o],val);else{
        if(tag[o])pushdown(o);
        const int mid=l+r>>1;
        if(pos<=mid)modify(l,mid,o<<1,pos,val);else modify(mid+1,r,o<<1|1,pos,val);
        d[o]=min(d[o<<1],d[o<<1|1]);
    }
}
void modify(int l,int r,int o,const int&L,const int&R,const int&val){
    if(L<=l&&r<=R){
        d[o]+=val,tag[o]+=val;
    }else{
        pushdown(o);
        const int mid=l+r>>1;
        if(L<=mid)modify(l,mid,o<<1,L,R,val);
        if(mid<R)modify(mid+1,r,o<<1|1,L,R,val);
        d[o]=min(d[o<<1],d[o<<1|1]);
    }
}
int query(int l,int r,int o,const int&L,const int&R){
    if(L<=l&&r<=R)return d[o];
    pushdown(o);
    const int mid=l+r>>1;
    if(L<=mid&&mid<R)return min(query(l,mid,o<<1,L,R),query(mid+1,r,o<<1|1,L,R));
    if(L<=mid)return query(l,mid,o<<1,L,R);
    return query(mid+1,r,o<<1|1,L,R);
}
int main(){
    ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
    cin>>n;
    for(int i=1;i<=n;++i)cin>>b[i],sum+=!b[i];
    for(cin>>m;m--;){
        int l,r;
        cin>>l>>r;
        vec[l].push_back(r);
    }
    build(0,n,1);
    modify(0,n,1,0,0);
    for(int i=1;i<=n;++i){
        for(int y:vec[i])modify(0,n,1,y,query(0,n,1,0,y));
        modify(0,n,1,0,i-1,b[i]?1:-1);
    }
    printf("%d\n",query(0,n,1,0,n)+sum);
    return 0;
}