题解【POJ2376】Cleaning Shifts

题面

题意简述:

(m) 个区间,每一段区间为 ([L_i,R_i]),现在有一个长度为 (n) 的线段,问最少选择多少个区间,才能够覆盖整个线段。

( exttt{Data Range: }1leq m leq 2.5 imes 10^4, 1leq n leq 10^6)

(dp_i) 表示只选择 (i) 左边的区间,覆盖区间 ([1,i]) 最少选择多少个区间。

假设当前区间左端点为 (l),右端点为 (r),那么状态的转移就是 (dp_r = minlimits_{l-1leq j < r}{dp_j} + 1)

然后可以使用一个单点修改、区间查询的线段树维护 (dp) 值。

#include <cstdio>
#include <algorithm>
#include <cstring>

using namespace std;

const int N = 25003, M = 1000003, INF = 0x3f3f3f3f;

int n, m;
struct Node
{
    int l, r;
} a[N];
int tr[M << 2];
int f[M];

inline bool cmp(Node x, Node y) 
{
    if (x.r != y.r) return x.r < y.r;
    return x.l < y.l;
}

inline int ls(int p) {return p << 1;}
inline int rs(int p) {return p << 1 | 1;}

inline void pushup(int p) {tr[p] = min(tr[ls(p)], tr[rs(p)]);}

void build(int l, int r, int p)
{
    tr[p] = INF;
    if (l == r) return;
    int mid = (l + r) >> 1;
    build(l, mid, ls(p)); build(mid + 1, r, rs(p));
}

void modify(int k, int v, int l, int r, int p)
{
    if (l == r) {tr[p] = min(tr[p], v); return;}
    int mid = (l + r) >> 1;
    if (k <= mid) modify(k, v, l, mid, ls(p));
    else modify(k, v, mid + 1, r, rs(p));
    pushup(p);
}

int getans(int ql, int qr, int l, int r, int p)
{
    if (ql <= l && r <= qr) return tr[p];
    int mid = (l + r) >> 1;
    int res = INF;
    if (ql <= mid) res = getans(ql, qr, l, mid, ls(p));
    if (qr > mid) res = min(res, getans(ql, qr, mid + 1, r, rs(p)));
    return res;
}

int main()
{
    scanf("%d%d", &m, &n);
    for (int i = 1; i <= m; i+=1) scanf("%d%d", &a[i].l, &a[i].r);
    build(0, n, 1);
    sort(a + 1, a + 1 + m, cmp);
    memset(f, 0x3f, sizeof f);
    f[0] = 0;
    modify(0, 0, 0, n, 1);
    for (int i = 1; i <= m; i+=1)
    {
        f[a[i].r] = min(f[a[i].r], getans(a[i].l - 1, a[i].r - 1, 0, n, 1) + 1);
        modify(a[i].r, f[a[i].r], 0, n, 1);
    }
    if (f[n] == INF) f[n] = -1;
    printf("%d
", f[n]);
    return 0;
}
原文地址:https://www.cnblogs.com/xsl19/p/12772302.html