题解:
当看到m只有3e5,而l,r的范围达到1e9就得有离散化的思想。
接着将线段按照行数优先,线段左端点优先的排序,接着处理每一行的线段间overlap问题;
我们先考虑留哪些行答案最大,用dp[i]表示在前i行中,第i行必留下所能总共留下的总行数。那么就可以得到状态转移方程为:
d
p
[
i
]
=
m
a
x
(
d
p
[
i
]
,
d
p
[
j
]
+
1
)
(
1
≤
j
<
i
)
(
当
然
前
提
是
第
i
行
和
第
j
行
有
相
交
的
线
段
)
dp[i]=max(dp[i],dp[j] + 1)(1\leq j < i)(当然前提是第i行和第j行有相交的线段)
dp[i]=max(dp[i],dp[j]+1)(1≤j<i)(当然前提是第i行和第j行有相交的线段)
可以看到复杂度巨大,来源于两方面,一是j的枚举,二是判断第i行和第j行相交的问题,如何进行优化?
假设离散化后的最大值为mx,那么对于j的枚举又该如何优化呢,我们维护一个横轴[1-mx],首先第1行[1-mx]中的所有线段在横轴上的位置答案都为1,接着到了第2行,枚举第2行的线段,看是否能与第1行的线段在横轴上所占的位置有所相交,一旦相交,那么第2行的所有线段所占的位置答案都更新为2,也就是说此时第1行所有线段中某些位置的值就都变为了2。但是没有相交的情况下,第2行的所有线段所占的位置答案也是1。接着考虑第3行,枚举第3行的线段,看横轴上与第3行所有线段相交的最大值是多少,假设为tmp,然后维护第3行所有线段所占的位置答案为tmp+1,接着枚举第4行,第5行,···,一直到第n行。这样就可以优化j的枚举问题,因为每次所查找能相交的最大值绝对是dp[i]-1。可以看到这就是线段树lazy赋值,和线段树取区间最大值的操作,其中也解决了判断相交的问题。复杂度也紧紧只是O(2mlogm)。
最后还有个是输出要删除的行,那么每次对于每一行所进行的更新,都用f[i]来记录,可以在线段树上除了记录区间最大值,也要记录区间最大值所在的行号,查询区间最大值后,并查询区间最大值所在的行号,每一行处理出dp[i]之后,并在线段树更新区间最大值和区间最大值所在的行号,记录出留下行号的路径。最后输出反向输出即可。
code:
#include <bits/stdc++.h>
#define lchild k<<1
#define rchild k<<1|1
using namespace std;
const int N = 6e5 + 5;
struct segment {
int row, l, r;
}ori_segments[N], final_segments[N];
int n, m, idx, l, r, tr[N << 2], lazy[N << 2], path[N << 2], lazy2[N << 2], mx, tot, lu[N];
bool cmp(segment a, segment b)
{
if (a.row != b.row) return a.row < b.row;
if (a.l != b.l) return a.l < b.l;
return a.r < b.r;
}
void pushdown(int k)
{
if (lazy[k] != 0) {
lazy[lchild] = lazy[k];
lazy[rchild] = lazy[k];
tr[lchild] = lazy[k];
tr[rchild] = lazy[k];
lazy[k] = 0;
}
if (lazy2[k] != 0) {
lazy2[lchild] = lazy2[k];
lazy2[rchild] = lazy2[k];
path[lchild] = lazy2[k];
path[rchild] = lazy2[k];
lazy2[k] = 0;
}
}
void update(int l, int r, int v, int row, int k = 1, int L = 1, int R = mx)
{
if (l == L && r == R) {
lazy[k] = v;
tr[k] = v;
lazy2[k] = row;
path[k] = row;
return ;
}
if (lazy[k] || lazy2[k]) pushdown(k);
int mid = (L + R) >> 1;
if (r <= mid) update(l, r, v, row, lchild, L, mid);
else if (l > mid) update(l, r, v, row, rchild, mid + 1, R);
else update(l, mid, v, row, lchild, L, mid), update(mid + 1, r, v, row, rchild, mid + 1, R);
tr[k] = max(tr[lchild], tr[rchild]);
path[k] = (tr[lchild] == tr[k] ? path[lchild] : path[rchild]);
}
int get(int l, int r, int k = 1, int L = 1, int R = mx)
{
if (l == L && r == R) return tr[k];
if (lazy[k] || lazy2[k]) pushdown(k);
int mid = (L + R) >> 1;
if (r <= mid) return get(l, r, lchild, L, mid);
else if (l > mid) return get(l, r, rchild, mid + 1, R);
else return max(get(l, mid, lchild, L, mid), get(mid + 1, r, rchild, mid + 1, R));
}
int get2(int l, int r, int v, int k = 1, int L = 1, int R = mx)
{
if (l == L && r == R) {
if (tr[k] == v) return path[k];
else return -1;
}
if (lazy[k] || lazy2[k]) pushdown(k);
int mid = (L + R) >> 1;
if (r <= mid) return get2(l, r, v, lchild, L, mid);
else if (l > mid) return get2(l, r, v, rchild, mid + 1, R);
else {
int f1 = get2(l, mid, v, lchild, L, mid), f2 = get2(mid + 1, r, v, rchild, mid + 1, R);
if (f1 != -1) return f1;
else return f2;
}
}
int main()
{
cin >> n >> m;
vector<int>numbers;
for (int i = 1; i <= m; i++) {
cin >> idx >> l >> r;
ori_segments[i].row = idx;
ori_segments[i].l = l;
ori_segments[i].r = r;
numbers.push_back(l);
numbers.push_back(r);
}
sort(numbers.begin(), numbers.end());
numbers.erase(unique(numbers.begin(), numbers.end()), numbers.end());
mx = numbers.size();
for (int i = 1; i <= m; i++) {
ori_segments[i].l = lower_bound(numbers.begin(), numbers.end(), ori_segments[i].l) - numbers.begin() + 1;
ori_segments[i].r = lower_bound(numbers.begin(), numbers.end(), ori_segments[i].r) - numbers.begin() + 1;
}
sort(ori_segments + 1, ori_segments + m + 1, cmp);
tot = 1;
for (int i = 1; i <= m; i++) {
if (i == 1) {
idx = ori_segments[1].row;
l = ori_segments[1].l;
r = ori_segments[1].r;
} else {
int nidx = ori_segments[i].row, nl = ori_segments[i].l, nr = ori_segments[i].r;
if(nidx == idx && nl >= l && nl <= r) {
r = max(r, nr);
} else {
final_segments[tot].row = idx;
final_segments[tot].l = l;
final_segments[tot].r = r;
tot++;
idx = nidx;
l = nl;
r = nr;
}
}
if (i == m) {
final_segments[tot].row = idx;
final_segments[tot].l = l;
final_segments[tot].r = r;
}
}
int cnt = 1, daan = 0, res;
for (int i = 1; i <= n; i++) {
int tmp = 0, beg = cnt;
while (cnt <= tot && final_segments[cnt].row == i) {
l = final_segments[cnt].l, r = final_segments[cnt].r;
if (get(l, r) > tmp) {
tmp = get(l, r);
lu[i] = get2(l, r, tmp);
}
cnt++;
}
for (int j = beg; j < cnt; j++) {
l = final_segments[j].l, r = final_segments[j].r;
update(l, r, tmp + 1, i);
if (tmp + 1 > daan) {
res = i;
daan = tmp + 1;
}
}
}
int ans = get(1, mx);
cout << n - ans << endl;
res = get2(1, mx, ans);
set<int>s;
s.insert(res);
while (lu[res] != 0) {
res = lu[res];
s.insert(res);
}
for (int i = 1; i <= n; i++) {
if (s.count(i) == 0) cout << i << " ";
}
return 0;
}