【CF833E】Caramel Clouds

【CF833E】Caramel Clouds

题面

洛谷

题目大意:

天上有\(n\)朵云,每朵云\(i\)会在时间\([li,ri]\)出现,你有\(C\)个糖果,你可以花费\(c_i\)个糖果让云\(i\)消失,同时需要保证你最多让两朵云消失.现在有\(m\)个独立的询问,每次给你一个需要让阳光照\(k\)时间的植
物,问你从时刻\(0\)开始,这个植物最快什么时候能长成.

\(n,m\leq 3e5\)

题解

这题好神仙啊。。。

我们首先记几个东西:

\(Free:\)表示当前空着的长度和

\(single[x]:\)表示只有\(x\)覆盖的长度

\(opt[x]:\)选出的两朵云中必定包含\(x\)的最大长度

\(Top:\)\(opt\)的最大值

\(cross[x][y]:\)表示只有\(x,y\)覆盖的长度

然后用一个\(set\)维护当前还未消失的云

分类讨论一下\(set\)中云的个数:

1、当\(set\)中没有云时,直接让\(Free\)加上贡献即可

2、当\(set\)中只有一朵云,更新\(single\)以及\(opt\)

3、当\(set\)中有两朵云,更新\(cross\)\(opt\)

4、当\(set\)中云朵数大于二,不用管,因为此时一定不会比云朵数小于等于二的时候答案更优

至于更新的方法,看代码应该能看懂

#include <iostream> 
#include <cstdio> 
#include <cstdlib> 
#include <cstring> 
#include <cmath> 
#include <algorithm>
#include <set>
#include <map> 
using namespace std; 
inline int gi() { 
    register int data = 0, w = 1; 
    register char ch = 0; 
    while (!isdigit(ch) && ch != '-') ch = getchar(); 
    if (ch == '-') w = -1, ch = getchar(); 
    while (isdigit(ch)) data = 10 * data + ch - '0', ch = getchar(); 
    return w * data; 
}
void chkmax(int &x, int y) { if (x < y) x = y; } 
const int MAX_N = 3e5 + 5;
struct Cloud { int l, r, c; } cld[MAX_N];
bool operator < (const Cloud &l, const Cloud &r) { return l.c < r.c; }
struct Query { int id, t; } q[MAX_N];
bool operator < (const Query &l, const Query &r) { return l.t < r.t; }
struct Node { int t, op, id; } a[MAX_N << 1]; int tot = 0; 
bool operator < (const Node &l, const Node &r) { return l.t < r.t; } 
int N, M, C, single[MAX_N], opt[MAX_N], ans[MAX_N];
map<int, int> cross[MAX_N];
set<int> s;
set<int>::iterator ite;
int Free, Top; 
#define lson (o << 1)
#define rson (o << 1 | 1)
int mx[MAX_N << 2];
void modify(int o, int l, int r, int pos, int v) {
    if (l == r) return (void)(mx[o] = v);
    int mid = (l + r) >> 1;
    if (pos <= mid) modify(lson, l, mid, pos, v);
    else modify(rson, mid + 1, r, pos, v);
    mx[o] = max(mx[lson], mx[rson]); 
}
int find(int o, int l, int r) {
    if (l == r) return l; 
    int mid = (l + r) >> 1;
    if (mx[lson] > mx[rson]) return find(lson, l, mid);
    else return find(rson, mid + 1, r); 
}
int query(int o, int l, int r, int ql, int qr) {
    if (ql <= l && r <= qr) return find(o, l, r); 
    int mid = (l + r) >> 1, res = 0; 
    if (ql <= mid) res = query(lson, l, mid, ql, qr); 
    if (qr > mid) {
        int tmp = query(rson, mid + 1, r, ql, qr); 
        if (single[tmp] > single[res]) res = tmp; 
    }
    return res; 
}
int sum(int x, int y) {
    if (x > y) swap(x, y); 
    return single[x] + single[y] + cross[x][y]; 
} 
void solve() {
    int now = 0, pos = 1; 
    for (int i = 1; i <= tot; i++) {
        int dlt = a[i].t - now; now = a[i].t;
        if (!s.size()) Free += dlt; 
        else if (s.size() == 1) {
            ite = s.upper_bound(0); int x = *ite;
            single[x] += dlt; modify(1, 1, N, x, single[x]);
            opt[x] += dlt; 
            int rem = C - cld[x].c; 
            if (rem >= 0) {
                int val = single[x]; 
                if (rem >= cld[1].c) {
                    int l = 1, r = N, res = 1; 
                    while (l <= r) {
                        int mid = (l + r) >> 1; 
                        if (cld[mid].c <= rem) res = mid, l = mid + 1; 
                        else r = mid - 1; 
                    }
                    int ql = 1, qr = res; 
                    if (x == qr) --qr;
                    if (x < qr) { ql = x + 1; if (ql <= qr) chkmax(val, sum(x, query(1, 1, N, ql, qr))); ql = 1, qr = x - 1; }
                    if (ql <= qr) chkmax(val, sum(x, query(1, 1, N, ql, qr))); 
                }
                chkmax(opt[x], val);
                chkmax(Top, opt[x]); 
            } 
        } else if (s.size() == 2) {
            ite = s.upper_bound(0); int x = *ite;
            ++ite; int y = *ite;
            if (cross[x].count(y) > 0) cross[x][y] += dlt;
            else cross[x][y] = dlt; 
            if (cld[x].c + cld[y].c <= C) { 
                chkmax(opt[x], sum(x, y)); chkmax(opt[y], sum(x, y));
                chkmax(Top, opt[x]); 
            } 
        }
        while (pos <= M && Top + Free >= q[pos].t) ans[q[pos].id] = now - (Top + Free - q[pos].t), ++pos;
        if (pos > M) break;
        if (a[i].op == 1) s.insert(a[i].id);
        else s.erase(a[i].id); 
    } 
} 
int main () {
    N = gi(), C = gi(); 
    for (int i = 1; i <= N; i++) cld[i].l = gi(), cld[i].r = gi(), cld[i].c = gi(); 
    sort(&cld[1], &cld[N + 1]);
    for (int i = 1; i <= N; i++) {
        a[++tot] = (Node){cld[i].l, 1, i};
        a[++tot] = (Node){cld[i].r, -1, i}; 
    }
    sort(&a[1], &a[tot + 1]);
    a[++tot] = (Node){(int)(2e9 + 7), 1, N + 1}; 
    M = gi(); 
    for (int i = 1; i <= M; i++) q[i].id = i, q[i].t = gi();
    sort(&q[1], &q[M + 1]);
    solve(); 
    for (int i = 1; i <= M; i++) printf("%d\n", ans[i]); 
    return 0;  
} 

转载于:https://www.cnblogs.com/heyujun/p/10207742.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值