Ezzat and Grid(离散化、线段树)

本文讲解了在处理线段覆盖问题时,如何通过离散化和线段树优化算法。重点介绍了状态转移方程的改进,利用线段树的懒惰评估技巧加速j的枚举,并记录区间最大值及其行号。核心优化使得复杂度降至O(2mlogm),并详细阐述了代码实现。
摘要由CSDN通过智能技术生成

传送门

题解:

当看到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)(1j<i)(ij线)

可以看到复杂度巨大,来源于两方面,一是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;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值