CF1557D Ezzat and Grid 题解

· · 题解

CF1557D Ezzat and Grid 题解

传送门

更高更妙的阅读体验

题意

给你一个 n\times m 的 01 串,让你删去最少的行(删去一行之后,上下两行变为相邻)使得任意相邻两行都有至少一个公共的位置上是 1.

### 题解 首先做一步小转化,从“删去最少”变成“选取最多”。 考虑DP,令 $f_{i}$​ 表示前 $i$​ 行最多选取几行,转移有 $$ f_i=\max_{j<i \land \text{第i行与第j行有公共的1}} f_{j}+1 $$ 注意到我们需要处理区间相交的问题,无疑线段树是合适的选择。 我们使用线段树来维护区间最大值:加入第 $i$ 行时,使用线段树求出与第 $i$ 有公共部分的最大值 $f_j,(j<i)$,完成 $f_j$ 到 $f_i$ 的转移之后再将第 $i$ 行有 $1$ 的位置全部更新成 $f_i$。 注意此题还要求输出方案,所以线段树上维护最大值的同时维护一下最大值来自于哪一行。 ### 代码 ```cpp #include<bits/stdc++.h> using namespace std; namespace Acc{ using pii=pair<int,int>; const int N=3e5+9; const pii NUL={0,-1}; int b[N<<1],L[N<<3],R[N<<3],las[N],f; pii v[N<<3],t[N<<3]; vector<pii>g[N]; #define lc (o<<1) #define rc (o<<1|1) void up(pii&x,pii y){if(x<y)x=y;} void bd(int o,int l,int r){ L[o]=l,R[o]=r,v[o]=t[o]=NUL; if(l==r)return; int md=l+r>>1; bd(lc,l,md),bd(rc,md+1,r); } void pd(int o){ if(t[o]==NUL)return; up(v[lc],t[o]),up(v[rc],t[o]); up(t[lc],t[o]),up(t[rc],t[o]),t[o]=NUL; } pii ask(int o,int l,int r){ if(l<=L[o] && R[o]<=r)return v[o]; pd(o); int md=L[o]+R[o]>>1;pii z=NUL; if(l<=md)up(z,ask(lc,l,r)); if(r>md)up(z,ask(rc,l,r)); v[o]=max(v[lc],v[rc]); return z; } void add(int o,int l,int r,pii x){ if(l<=L[o] && R[o]<=r)return (void)(up(v[o],x),up(t[o],x)); int md=L[o]+R[o]>>1;pd(o); if(l<=md)add(lc,l,r,x); if(r>md)add(rc,l,r,x); v[o]=max(v[lc],v[rc]); } void work(){ int n,m,l=0,x,y,z,i; for(cin>>n>>m;m--;) cin>>x>>y>>z,g[x].push_back({y,z}),b[l++]=y,b[l++]=z; sort(b,b+l),l=unique(b,b+l)-b,bd(1,1,l); for(i=1;i<=n;++i)for(auto&[x,y]:g[i]) x=lower_bound(b,b+l,x)-b+1,y=lower_bound(b,b+l,y)-b+1; for(i=1;i<=n;++i){ pii mx=NUL; if(i==5)f=1; for(auto[x,y]:g[i])mx=max(mx,ask(1,x,y)); las[i]=mx.second,mx.second=i,++mx.first; for(auto[x,y]:g[i])add(1,x,y,mx); } pii mx=ask(1,1,l);cout<<n-mx.first<<'\n'; for(x=mx.second;~x;x=las[x])b[x]=-1; for(i=1;i<=n;++i)if(~b[i])cout<<i<<' '; } } int main(){return Acc::work(),0;} ``` ### 小细节 此题的值域较大,需要离散化。