定义 f [ i ] f[i] f[i]表示以第 i i i行结尾,最少删掉的行数
若第 i i i行与第 j j j行有交,有转移
f [ i ] = f [ j ] + ( i − j − 1 ) f[i]=f[j]+(i-j-1) f[i]=f[j]+(i−j−1)
但是显然不能暴力枚举 j j j,而且判断线段是否有交也得枚举,也是个大问题
考虑 j 1 , j 2 j_1,j_2 j1,j2行都覆盖了点 w w w,下次某一行 i i i如果覆盖了点 w w w,显然是从 j 1 , j 2 j_1,j_2 j1,j2挑一个代价更小的转移而来
然后再注意转移方程,其实可以写成
f [ i ] = ( f [ j ] − j ) + ( i − 1 ) f[i]=(f[j]-j)+(i-1) f[i]=(f[j]−j)+(i−1)
显然 f [ j ] − j f[j]-j f[j]−j看成一个整体,那么对后续的贡献都是相同的
于是容易想到,维护一颗线段树,线段树的叶子节点 w w w表示覆盖 w w w的最优秀的 f [ j ] − j f[j]-j f[j]−j
这样,转移 第 i 第i 第i行的时候,枚举所有线段在区间上取 m i n min min即可
最后枚举以 i i i结尾答案就是需要删掉 f [ i ] + ( n − i ) f[i]+(n-i) f[i]+(n−i)(因为后面也需要删掉)
#include <bits/stdc++.h>
using namespace std;
#define se second
#define mid (l+r>>1)
#define ls (rt<<1)
#define rs (rt<<1|1)
#define lson ls,l,mid
#define rson rs,mid+1,r
const int maxn = 6e5+10;
const int inf = 1e9;
typedef pair<int,int>p;
p now;
vector<p>vec[maxn];
int f[maxn],pre[maxn],ok[maxn],li[maxn],top,z,n,m;
void read()
{
cin >> n >> m;
for(int i=1;i<=m;i++)
{
int id,l,r; scanf("%d%d%d",&id,&l,&r);
vec[id].push_back( {l,r} );
li[++li[0]] = l, li[++li[0]] = r;
}
sort( li+1,li+1+li[0] );
top = unique( li+1,li+1+li[0] )-li-1;
for(int i=1;i<=n;i++)
{
for(int j=0;j<vec[i].size();j++)
{
vec[i][j].first = lower_bound( li+1,li+1+top,vec[i][j].first )-li;
vec[i][j].se = lower_bound( li+1,li+1+top,vec[i][j].se )-li;
}
}
}
p sum[maxn<<2],laz[maxn<<2];
void pushdown(int rt,int l,int r)
{
if( laz[rt].first==inf ) return;
sum[ls] = min( sum[ls],laz[rt] ); sum[rs] = min( sum[rs],laz[rt] );
laz[ls] = min( laz[ls],laz[rt] ); laz[rs] = min( laz[rs],laz[rt] );
laz[rt] = {inf,0};
}
void update(int rt,int l,int r,int L,int R,p val)
{
if( l>R || r<L ) return;
if( l>=L && r<=R )
{
sum[rt] = min( sum[rt],val ); laz[rt] = min( laz[rt],val );
return;
}
pushdown(rt,l,r);
update( lson,L,R,val ); update( rson,L,R,val );
sum[rt] = min( sum[ls],sum[rs] );
}
void get(int rt,int l,int r,int L,int R)
{
if( l>R || r<L ) return;
if( l>=L && r<=R ) { now = min( now,sum[rt]); return; }
pushdown(rt,l,r);
get( lson,L,R ); get( rson,L,R );
}
void solve()
{
int mi = inf, id = 0;
for(int i=1;i<=n;i++)
{
if( f[i]+(n-i)<mi )
mi = f[i]+(n-i), id = i;
}
while( id ) ok[id] = 1, id = pre[id];
cout << mi << endl;
for(int i=1;i<=n;i++)
if( ok[i]==0 ) cout << i << " ";
}
void DP()
{
for(int i=1;i<=4*top;i++) sum[i] = {0,0},laz[i] = {inf,0};
for(int i=1;i<=n;i++)
{
if( vec[i].size()==0 ) { f[i] = inf; continue; }
z++;
now = {inf,0};
for(auto v:vec[i] )
get(1,1,top,v.first,v.se );
f[i] = now.first+i-1, pre[i] = now.second;
for(auto v:vec[i] )
update( 1,1,top,v.first,v.se,{ f[i]-i,i } );
}
}
signed main()
{
read();
DP();
solve();
}