题意
有 n n n行全0段,将其中 m m m段赋值为1,问满足条件的行数最多是多少。条件为:两行至少有一列全1。
分析
类似最长上升子序列从前向后转移。
每次当前行选出一个满足条件的最大行进行转移,上述找满足条件的值最大行的操作可以使用线段树优化。
由于数据范围在 1 e 9 1e^9 1e9之内,需要离散化。
需要一颗区间赋值,求区间最值的线段树,除值外线段树还需存当前值对应的行数,可以使用 p a i r pair pair进行存储。
按照行从小到大的顺序操作,先更新答案,再对当前区间赋值为当前答案最大值+1。
如果对于当前行的一段1来说,如果这段1对应的列数在之前为出现1,则仅看这段1,当前行答案为1。如果在之前出现过,则答案为 q u e r y _ m a x ( l , r ) + 1 query\_max(l,r)+1 query_max(l,r)+1。
由于需要输出方案,使用 p r e pre pre数组记录路径。
时间复杂度 O ( n l o g n ) O(nlogn) O(nlogn)
代码
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
#define endl '\n'
ll gcd(ll a, ll b) { return b == 0 ? a : gcd(b, a % b); }
ll lcm(ll a, ll b) { return a / gcd(a, b) * b; }
void input() { freopen("in.txt", "r", stdin), freopen("out.txt", "w", stdout); }
const int N = 1e6 + 10, M = N * 2, inf = 1e8;
struct Node{
int l, r;
pii v, lazy;
}tr[N * 4];
pii Max(pii a, pii b){
if(a.first > b.first) return a;
return b;
}
void pushup(int u){
tr[u].v = Max(tr[u<<1].v, tr[u<<1|1].v);
}
void pushdown(int u){
if(tr[u].lazy.first != 0){
tr[u<<1].lazy = tr[u<<1|1].lazy = tr[u].lazy;
tr[u<<1].v = tr[u<<1|1].v = tr[u].lazy;
tr[u].lazy = {0, 0};
}
}
void build(int u, int l, int r){
tr[u].l = l, tr[u].r = r;
if(l == r) return;
else{
int mid = (l + r) >> 1;
build(u << 1, l, mid);
build(u << 1 | 1, mid + 1, r);
pushup(u);
}
}
void update(int u, int l, int r, pii v){
if(l <= tr[u].l && tr[u].r <= r){
tr[u].v = v;
tr[u].lazy = v;
}else{
pushdown(u);
int mid = (tr[u].l + tr[u].r) >> 1;
if(l <= mid) update(u <<1, l, r, v);
if(r > mid) update(u<<1|1, l, r, v);
pushup(u);
}
}
pii query(int u, int l, int r){
if(l <= tr[u].l && tr[u].r <= r){
return tr[u].v;
}else{
pushdown(u);
int mid = (tr[u].l + tr[u].r) >> 1;
pii res = {0, 0};
if(l <= mid) res = Max(res, query(u<<1, l, r));
if(r > mid) res = Max(res, query(u<<1|1, l, r));
return res;
}
}
int cnt, b[N], n, m, pre[N], vis[N];
vector<pii> G[N];
struct Q{
int id, l, r;
}q[N];
int main() {
ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
cin>>n>>m;
for(int i = 1; i <= m; i++){
cin>>q[i].id>>q[i].l>>q[i].r;
b[++cnt] = q[i].l, b[++cnt] = q[i].r;
}
sort(b + 1, b + cnt + 1);
cnt = unique(b + 1, b + cnt + 1) - b - 1;
for(int i = 1; i <= m; i++){
q[i].l = lower_bound(b + 1, b + cnt + 1, q[i].l) - b;
q[i].r = lower_bound(b + 1, b + cnt + 1, q[i].r) - b;
G[q[i].id].push_back({q[i].l, q[i].r});
}
build(1, 1, cnt);
int flag = 0, ans = 0;
for(int i = 1; i <= n; i++){
int now = -1;
for(auto it : G[i]){
pii t = query(1, it.first, it.second);
if(t.first > now){
now = t.first;
pre[i] = t.second;
}
}
if(now + 1 > ans){
ans = now + 1;
flag = i;
}
for(auto it : G[i]){
update(1, it.first, it.second, {now + 1, i});
}
}
cout<<n-ans<<endl;
for(;flag != 0; flag = pre[flag]) vis[flag] = 1;
for(int i = 1; i <= n; i++) if(vis[i] == 0) cout<<i<<" ";
return 0;
}