题意
给定 n n n 行 1 0 9 10^9 109 列的 01 矩阵。第 i i i 行和 i + 1 i+1 i+1 行是相邻的当且仅当至少存在一列,这两行这一列的数都是 1。问最少删掉多少行,才能使对于每个 1 ≤ i < m 1\le i<m 1≤i<m,第 i i i 行和 i + 1 i+1 i+1 行都相邻。 m m m 是删掉之后的总行数。输出方案。 1 ≤ n , m ≤ 3 × 1 0 5 1\le n,m\le 3\times10^5 1≤n,m≤3×105
思路
-
正难则反,考虑剩余的最长长度,类似最长上升子序列,本题的 dp 转移方程:
f i = max 1 ≤ j < i , g ( i , j ) f j + 1 f_i=\max_{1\le j < i,g(i,j)} f_j+1 fi=1≤j<i,g(i,j)maxfj+1
其中 g ( i , j ) g_(i,j) g(i,j) 表示第 i i i 行与第 j j j 行有至少一列为 1 -
类似最长上升子序列,需要有一个数据结构来维护 [ 1 , i − 1 ] [1,i-1] [1,i−1] 的最值,因为要输出方案,还要维护从 j j j 转移到 i i i 时 j j j 的编号,考虑线段树
-
值域 1 0 9 10^9 109,考虑离散化,然后建树
-
处理的细节:
- 查询的时候要查两个值,用全局变量维护,注意每次循环的初始化。
- 更新完 dp 值后,要把当前行所有段都更新一遍
- 注意建树的大小,是离散化后的大小。注意数组要开 2*n,因为对区间两个端点离散化
- 老老实实用懒标记,pushdown 别忘了懒标记,query 不要 pushup
代码
const int N = 6e5 + 5; //注意是 2 * n
int n, m, cnt, f[N], pre[N];
int mx, pos;
struct Node {int id, x, y;} p[N];
struct node {int x, y;};
vector<node> a[N];
set<int> s, ans;
map<int, int> mp;
struct tree {int mx, id; bool tag;} t[N << 2];
void pushdown(int u) {
if(!t[u].tag) return;
t[ls].mx = t[rs].mx = t[u].mx;
t[ls].id = t[rs].id = t[u].id;
t[ls].tag = t[rs].tag = t[u].tag;
t[u].tag = false;
}
void pushup(int u) {
if(t[ls].mx >= t[rs].mx) {
t[u].mx = t[ls].mx;
t[u].id = t[ls].id;
} else {
t[u].mx = t[rs].mx;
t[u].id = t[rs].id;
}
}
void upd(int u, int l, int r, int x, int y, int val, int id) {
if(x <= l && r <= y) {
t[u].mx = val;
t[u].id = id;
t[u].tag = true;
return;
}
pushdown(u);
int mid = (l + r) >> 1;
if(x <= mid) upd(ls, l, mid, x, y, val, id);
if(y > mid) upd(rs, mid + 1, r, x, y, val, id);
pushup(u);
}
void query(int u, int l, int r, int x, int y) {
if(x <= l && r <= y) {
if(t[u].mx >= mx) {
mx = t[u].mx;
pos = t[u].id;
}
return;
}
pushdown(u);
int mid = (l + r) >> 1;
if(x <= mid) query(ls, l, mid, x, y);
if(y > mid) query(rs, mid + 1, r, x, y);
}
int main() {
cin >> n >> m;
rep(i, 1, m) {
p[i] = {rd, rd, rd};
s.insert(p[i].x), s.insert(p[i].y);
}
for(auto ele : s) mp[ele] = ++cnt;
rep(i, 1, m) a[p[i].id].pb({mp[p[i].x], mp[p[i].y]});
rep(i, 1, n) {
for(auto ele : a[i]) {
int x = ele.x, y = ele.y;
mx = pos = 0;
query(1, 1, cnt, x, y);
if(mx + 1 > f[i]) {
f[i] = mx + 1;
pre[i] = pos;
}
}
for(auto ele : a[i]) upd(1, 1, cnt, ele.x, ele.y, f[i], i);
}
int now = max_element(f + 1, f + n + 1) - f;
rep(i, 1, n) ans.insert(i);
while(now) {
ans.erase(now);
now = pre[now];
}
cout << sz(ans) << endl;
for(auto ele : ans) cout << ele << ' ';
return 0;
}