題解 CF1557D Ezzat and Grid
阿新 • • 發佈:2021-08-10
這題要是場切可改命了啊!
太可惜了,如此接近正解。
給定 \(n\) 行無限長的 01 串,求最少刪除幾個滿足相鄰的上下有連續 \(1\),輸出方案。
死就死在這個輸出方案上。
看到題先考慮 dp,\(f_i\) 是到 \(i\) 的答案。
得到方程 \(f_i = \min \{f_j + i - j - 1\}\),條件是 \(i\) 和 \(j\) 要能連上,移項得 \(f_i = i + \min\{f_j - j - 1\}\),這個 \(\min\) 用線段樹維護就好了,區間修改區間查詢。
但是!但是!但是!構造方案不能直接像揹包一樣用 \(f\) 陣列倒推啊!!!
這裡的轉移並不是沒有限制的!得老老實實記錄轉移來的地方啊!
痛失 D!
警告!
在 dp 輸出方案中,請記錄轉移來的狀態,而不是根據最後答案來倒推!
程式碼
#include <iostream> #include <algorithm> #include <vector> #include <utility> #define fi first #define se second #define mapa std::make_pair const int N = 600005, INF = 0x3f3f3f3f; int n, m, ans = INF, MAX, f[N], flag[N], ai, pre[N]; std::pair<int, int> t[4*N], tag[4*N]; std::vector<int> b; std::vector<std::pair<int, int> > li[N]; void pushdown(int p) { t[p+p] = std::min(t[p+p], tag[p]); tag[p+p] = std::min(tag[p+p], tag[p]); t[p+p+1] = std::min(t[p+p+1], tag[p]); tag[p+p+1] = std::min(tag[p+p+1], tag[p]); tag[p] = mapa(INF, 0); } void pushup(int p) { t[p] = std::min(t[p+p], t[p+p+1]); } std::pair<int, int> Query(int p, int l, int r, int x, int y) { if (l == x && r == y) return t[p]; pushdown(p); int mid = l + (r-l) / 2; if (y <= mid) return Query(p+p, l, mid, x, y); else if (x > mid) return Query(p+p+1, mid+1, r, x, y); else return std::min(Query(p+p, l, mid, x, mid), Query(p+p+1, mid+1, r, mid+1, y)); } void Update(int p, int l, int r, int x, int y, std::pair<int, int> z) { if (l == x && r == y) { t[p] = std::min(t[p], z), tag[p] = std::min(tag[p], z); return; } pushdown(p); int mid = l + (r-l) / 2; if (y <= mid) Update(p+p, l, mid, x, y, z); else if (x > mid) Update(p+p+1, mid+1, r, x, y, z); else Update(p+p, l, mid, x, mid, z), Update(p+p+1, mid+1, r, mid+1, y, z); pushup(p); } void build(int p, int l, int r) { if (l == r) { t[p] = mapa(-1, 0), tag[p] = mapa(INF, 0); return; } int mid = l + (r-l) / 2; build(p+p, l, mid), build(p+p+1, mid+1, r); pushup(p); } int main() { std::cin >> n >> m; for (int i = 1, x, l, r; i <= m; i++) { std::cin >> x >> l >> r; li[x].push_back(mapa(l, r)); b.push_back(l), b.push_back(r); } std::sort(b.begin(), b.end()); MAX = b.size() + 1; b.erase(std::unique(b.begin(), b.end()), b.end()); for (int i = 1; i <= n; i++) for (int j = 0; j < (int)li[i].size(); j++) li[i][j].fi = std::lower_bound(b.begin(), b.end(), li[i][j].fi) - b.begin() + 1, li[i][j].se = std::lower_bound(b.begin(), b.end(), li[i][j].se) - b.begin() + 1; build(1, 1, MAX); for (int i = 1; i <= n; i++) { int min = INF, pr = 0; for (int j = 0; j < (int)li[i].size(); j++) { std::pair<int, int> now = Query(1, 1, MAX, li[i][j].fi, li[i][j].se); if (now.fi < min) min = now.fi, pr = now.se; } int fi = min + i; f[i] = fi, pre[i] = pr; if (fi + n-i < ans) ans = fi + n-i, ai = i; for (int j = 0; j < (int)li[i].size(); j++) Update(1, 1, MAX, li[i][j].fi, li[i][j].se, mapa(fi - i - 1, i)); } while (ai) { flag[ai] = 1; ai = pre[ai]; } std::cout << ans << '\n'; for (int i = 1; i <= n; i++) if (!flag[i]) std::cout << i << ' '; }
這比賽算是給我提了個醒了!