1. 程式人生 > >【Luogu P4644】Cleaning Shifts

【Luogu P4644】Cleaning Shifts

set line shift clu 做了 arrow scan 樹狀 代碼

題目

給定 \(n\) 個區間 \([a_i, b_i]\), 花費為 \(c_i\), 求覆蓋 \([L, R]\) 區間的所有整數的最小花費. \(0\le n \le 10^4, 0\le L,R\le 86399\)

分析

一道很水的題目.

\(f(i)\) 代表目前選擇了第 \(i\) 個區間, 且第 \(i\) 個區間以前的的所有數都選擇到了.

易得:
\[ f(i) = \min_{b_j > a_i, b_i < b_j} f(j) + c_i \]
一看, 這不就是裸的二維偏序 (其實我並不知道二維偏序的定義是啥).

先把區間按 \(b_i\) 排序, 得到:
\[ f(i) = \min_{b_j > a_i} f(j) + c_i \]


顯然有:
\[ b_j > a_i \Rightarrow X-b_j < X-a_i \]
其中 \(X\) 隨便取一個較大的值.

發現這個東西只做了單點減少和前綴最小值.

可以用樹狀數組維護, 時間復雜度 \(O(R\log n)\).

\(92\ ms\) 就過了.

代碼

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

const int kMaxSize = 1e6 + 5, inf = 0x3f3f3f3f;

int s[kMaxSize + 233], n;

void Modify(int x, int y) {
    x = kMaxSize - x;
    while(x <= kMaxSize) {
        s[x] = std::min(s[x], y);
        x += x & -x;
    }
}

int Query(int x) {
    x = kMaxSize - x;
    int ret = inf;
    while(x > 0) {
        ret = std::min(s[x], ret);
        x -= x & -x;
    }
    return ret;
}

struct Struct {
    int a, b, c;
} p[kMaxSize];

bool cmp(Struct x, Struct y) {
    return x.b < y.b;
}

int f[kMaxSize], ans = inf;
int main() {
    memset(s, 0x3f, sizeof(s));
    memset(f, 0x3f, sizeof(f));
    int l, r;
    scanf("%d%d%d", &n, &l, &r);
    for(int i = 1; i <= n; i++)
        scanf("%d%d%d", &p[i].a, &p[i].b, &p[i].c);
    std::sort(p + 1, p + n + 1, cmp);
    for(int i = 1; i <= n; i++) {
        if(p[i].a > l) f[i] = Query(p[i].a - 1) + p[i].c;
        else f[i] = p[i].c;
        Modify(p[i].b, f[i]);
        if(p[i].b >= r) ans = std::min(ans, f[i]);
    }
    if(ans >= inf) printf("-1");
    else printf("%d", ans);
    return 0;
}

【Luogu P4644】Cleaning Shifts