1. 程式人生 > 其它 >題解 CF1557D Ezzat and Grid

題解 CF1557D Ezzat and Grid

這題要是場切可改命了啊!
太可惜了,如此接近正解。

給定 \(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 << ' ';
}

這比賽算是給我提了個醒了!