[DP记录]AT3673 [ARC085D] NRE

command_block

2021-05-02 13:52:12

Personal

**题意** :给定长为 $n$ 的 $01$ 数组 $b$ 。 有一个长度同为 $n$ 的数组 $A$ ,初始时为全 $0$。 给定 $m$ 个区间,可以选择若干区间,将 $a$ 中区间内的元素置为 $1$。 最小化 $\sum\limits_{i=1}^n[A_i\neq B_i]$ 的值。 $n\leq 2\times 10^5$ ,时限 $\texttt{3s}$。 ------------ 注意到 $A,B$ 均为 $01$ 数组,则 $$ \begin{aligned} &[A_i\neq B_i]\\ =&[A_i=0][B_i=1]+[A_i=1][B_i=0]\\ =&[A_i=0][B_i=1]+[B_i=0]-[A_i=0][B_i=0]\\ \end{aligned} $$ 此时不需要考虑 $A_i=1$ 的贡献。 > 只有 $01\ \rightarrow$ 正难则反 其中 $[B_i=0]$ 是定值,于是我们只需最小化 : $$\sum\limits_{i=1}^n[A_i=0][B_i=1]-[A_i=0][B_i=0]$$ 考虑 $\rm DP$ ,记 $f[i][j]$ 表示考虑了所有 $l\leq i$ 的区间,最右覆盖到 $j$ , $[1,i]$ 的贡献最小值。 特殊地,若没有向右覆盖,则 $j$ 记为 $i$ 。 从 $f[i-1][\_]$ 转移至 $f[i][\_]$ 时 : 不选区间 : $$f[i][i]\leftarrow f[i-1][i-1]+[B_i=1]-[B_i=0]$$ $$f[i][j]\leftarrow f[i-1][j]\quad(j\geq i)$$ 枚举区间 $[i,r]$ ,有 : $$f[i][r]\leftarrow f[i-1][i-1\sim r]$$ 可以用线段树维护,复杂度为 $O(n\log n)$。 ```cpp #include<algorithm> #include<cstdio> #include<vector> #define MaxN 200500 using namespace std; int n,a[MaxN<<2],x[MaxN],to,wfc,wfl,wfr,ret; void build(int l=0,int r=n,int u=1) { x[l]=a[u]=(!l ? 0 : n); if (l==r)return ; int mid=(l+r)>>1; build(l,mid,u<<1); build(mid+1,r,u<<1|1); } void chg(int l=0,int r=n,int u=1) { if (l==r){x[l]=a[u]=min(x[l],wfc);return ;} int mid=(l+r)>>1; if (to<=mid)chg(l,mid,u<<1); else chg(mid+1,r,u<<1|1); a[u]=min(a[u<<1],a[u<<1|1]); } void qry(int l=0,int r=n,int u=1) { if (wfl<=l&&r<=wfr) {ret=min(ret,a[u]);return ;} int mid=(l+r)>>1; if (wfl<=mid)qry(l,mid,u<<1); if (mid<wfr)qry(mid+1,r,u<<1|1); } vector<int> l[MaxN]; int m,b[MaxN]; int main() { scanf("%d",&n);build(); for (int i=1;i<=n;i++)scanf("%d",&b[i]); scanf("%d",&m); for (int i=1,tl,tr;i<=m;i++){ scanf("%d%d",&tl,&tr); l[tl].push_back(tr); } for (int i=1;i<=n;i++){ for (int j=0;j<l[i].size();j++){ ret=n;wfl=i-1;wfr=l[i][j];qry(); to=l[i][j];wfc=ret;chg(); } wfc=x[i-1]+(b[i]==1)-(b[i]==0); to=i;chg(); } wfl=wfr=n;ret=n;qry(); for (int i=1;i<=n;i++)ret+=(b[i]==0); printf("%d",ret); return 0; } ```