1 条题解

  • 0
    @ 2026-10-27 4:21:40

    思路与算法

    把每个可完成挑战写成区间[l=x-y+1,r=x],y>k者永不触发可删除。最优连续跑步段可以从某个挑战的左端开始,否则删去无贡献前缀更优,因此压缩所有l-1及r和0。线段树下标j表示最近休息日,值为之前已休息的最优收益,减去从j到当前日的跑步成本,加上当前这段内完全覆盖的已结束挑战奖励。按事件日递增:全体减d*天数差,插入当天休息状态为先前best,对结束于当天的挑战给j≤l-1区间加v,查询j≥当前日-k且j<当前日的最大值并更新best。

    正确性说明

    任一合法安排可分为休息分隔的跑步段。固定最后休息日j后,后面每天跑步,成本和挑战奖励完全由j与当前日决定,状态无需保留更早细节;此前状态的最优收益可独立拼接。挑战在结束日恰对j<l的所有状态贡献,区间加准确。j下界保证连续不超过k天,排除当天休息点防止休息同时领奖。最优段首可缩到首个已领奖挑战的左端,故压缩坐标包含必要断点;全体减费用处理空白天不会漏计。取历史best也涵盖随时停止或从不跑步。

    复杂度

    每组O(m log m)时间、O(m)空间,含排序、去重及线段树初始化;T组总O(∑m log m)。

    易错点

    每天成本是d,不是仅发生挑战的那几天付费;休息至少一天才能重置连续天数。能量可负,但全不跑答案0始终可行。天数乘费用最高1e18,用64位并保留安全负无穷。

    参考代码(C++11)

    #include <bits/stdc++.h>
    using namespace std;
    using ll = long long;
    const ll NEG = -(1LL << 61);
    struct Seg {
        int n;
        vector<ll> mx, lz;
        Seg(int n) : n(n), mx(4 * n, NEG), lz(4 * n) {
        }
        void apply(int u, ll v) {
            mx[u] += v;
            lz[u] += v;
        }
        void push(int u) {
            if (lz[u]) {
                apply(2 * u, lz[u]);
                apply(2 * u + 1, lz[u]);
                lz[u] = 0;
            }
        }
        void add(int u, int l, int r, int L, int R, ll v) {
            if (L <= l && r <= R) {
                apply(u, v);
                return;
            }
            push(u);
            int m = (l + r) / 2;
            if (L <= m)
                add(2 * u, l, m, L, R, v);
            if (R > m)
                add(2 * u + 1, m + 1, r, L, R, v);
            mx[u] = max(mx[2 * u], mx[2 * u + 1]);
        }
        void setval(int u, int l, int r, int p, ll v) {
            if (l == r) {
                mx[u] = v;
                lz[u] = 0;
                return;
            }
            push(u);
            int m = (l + r) / 2;
            if (p <= m)
                setval(2 * u, l, m, p, v);
            else
                setval(2 * u + 1, m + 1, r, p, v);
            mx[u] = max(mx[2 * u], mx[2 * u + 1]);
        }
        ll query(int u, int l, int r, int L, int R) {
            if (L > R)
                return NEG;
            if (L <= l && r <= R)
                return mx[u];
            push(u);
            int m = (l + r) / 2;
            ll v = NEG;
            if (L <= m)
                v = max(v, query(2 * u, l, m, L, R));
            if (R > m)
                v = max(v, query(2 * u + 1, m + 1, r, L, R));
            return v;
        }
    };
    int main() {
        ios::sync_with_stdio(false);
        cin.tie(nullptr);
        int c, T;
        cin >> c >> T;
        while (T--) {
            int n, m, k;
            ll d;
            cin >> n >> m >> k >> d;
            struct Event {
                int r, l;
                ll v;
            };
            vector<Event> events;
            vector<int> pos{0};
            for (int i = 0; i < m; i++) {
                int x, y;
                ll v;
                cin >> x >> y >> v;
                if (y <= k) {
                    events.push_back({x, x - y + 1, v});
                    pos.push_back(x);
                    pos.push_back(x - y);
                }
            }
            sort(events.begin(), events.end(), [](Event a, Event b) {
                return a.r < b.r;
            });
            sort(pos.begin(), pos.end());
            pos.erase(unique(pos.begin(), pos.end()), pos.end());
            int N = pos.size(), at = 0, last = 0;
            ll best = 0;
            Seg tree(N);
            for (int i = 0; i < N; i++) {
                int t = pos[i];
                tree.apply(1, -d * (t - last));
                tree.setval(1, 0, N - 1, i, best);
                while (at < (int)events.size() && events[at].r == t) {
                    auto e = events[at++];
                    int R = upper_bound(pos.begin(), pos.end(), e.l - 1) - pos.begin() - 1;
                    tree.add(1, 0, N - 1, 0, R, e.v);
                }
                int L = lower_bound(pos.begin(), pos.end(), t - k) - pos.begin();
                if (L < i)
                    best = max(best, tree.query(1, 0, N - 1, L, i - 1));
                last = t;
            }
            cout << best << "\n";
        }
    }
    
    • 1

    【23NOIP提高组】天天爱打卡(run)

    信息

    ID
    2001
    时间
    2000ms
    内存
    512MiB
    难度
    提高
    标签
    递交数
    0
    已通过
    0
    上传者