JOISC2024 Day1A Fish 另解

By hhoppitree.

JOISC2024 Day1A. Fish

题目大意

给定一个长度为 N 的序列 CQ 次询问区间 [Li,Ri],表示若初始有一个长度为 RiLi+1 的全 0 数组 A,每次操作可单点加 D 或者将一个后缀加上 1,求最少单点加操作次数使得 Ai=CL+i1,或输出无解。
数据范围:N,Q3×1052s/1GB

思路分析

考虑将操作倒序进行,那么相当于将每次询问的子段进行最少的单点减 D 操作使得其非严格不降,这可以从后到前贪心,得到了一个 O(NQ) 的算法。

考虑使用线段树维护,那么我们可以先将拆分出的每个段内先进行调整后再合并调整,例如段 [L,mid),[mid,R),可以先将 [L,mid)[mid,R) 分别调整后对 [L,mid) 的后缀进行调整。

调整可以被拆成若干个后缀减 D,我们只需对于每一个线段树的区间 [L,R] 快速计算要求 CRx 时最少的操作次数以及此时 CL 的值即可。

i,i+1(经过提前调整后必然有 CiCi+1) 之间视作有 Ci+1CiD 个空隙,CL 前有 个空隙,那么每次就是将 [L,R] 的最靠后的一个空隙对应的后缀进行后缀减 D

而我们要进行 xCRD 次这样的操作,后缀和后二分计算出对应的值即可。

时间复杂度为 O(Nlog2N),将询问离线后归并计算可以做到 O(NlogN)(视 N,Q 同阶)。

代码呈现

实现了 O(Nlog2N) 的算法。

#include <bits/stdc++.h>

using namespace std;

const int N = 3e5 + 5;

long long a[N], D;
vector<long long> arr[1 << 20];
long long opt[1 << 20], sc[1 << 20], pos[1 << 20];
vector< tuple<long long, long long, long long> > z[1 << 20];

void upd(int k) {
    for (int i = 0; i + 1 < (int)arr[k].size(); ++i) {
        z[k].push_back({sc[k], i + 1, pos[k]});
        sc[k] += (arr[k][i] - arr[k][i + 1]) / D;
        pos[k] += (arr[k][i] - arr[k][i + 1]) / D * (i + 1);
    }
}

void build(int k, int l, int r) {
    if (l == r) {
        arr[k].push_back(a[l]);
        upd(k);
        return;
    }
    int mid = (l + r) >> 1;
    build(k << 1, l, mid);
    build(k << 1 | 1, mid + 1, r);
    opt[k] = opt[k << 1] + opt[k << 1 | 1];
    arr[k] = arr[k << 1 | 1];
    for (auto x : arr[k << 1]) {
        if (x > arr[k].back()) {
            opt[k] += ((x - arr[k].back() + D - 1) / D);
            x -= D * ((x - arr[k].back() + D - 1) / D);
        }
        arr[k].push_back(x);
    }
    upd(k);
}

pair<long long, long long> query(int k, int l, int r, int x, int y, long long w) {
    if (l >= x && r <= y) {
        if (w >= arr[k][0]) return {arr[k].back(), opt[k]};
        long long o = (arr[k][0] - w + D - 1) / D;
        if (sc[k] <= o) {
            return {arr[k].back() - (o - sc[k]) * D, opt[k] + pos[k] + (o - sc[k]) * (r - l + 1)};
        }
        auto [a, b, c] = *--upper_bound(z[k].begin(), z[k].end(), tuple<long long, long long, long long>{o, 0, 0});
        return {arr[k].back(), opt[k] + c + (o - a) * b};
    }
    int mid = (l + r) >> 1;
    if (y <= mid) return query(k << 1, l, mid, x, y, w);
    if (x > mid) return query(k << 1 | 1, mid + 1, r, x, y, w);
    pair<long long, long long> rs = query(k << 1 | 1, mid + 1, r, x, y, w);
    pair<long long, long long> ls = query(k << 1, l, mid, x, y, rs.first);
    return {ls.first, ls.second + rs.second};
}

signed main() {
    int n; scanf("%d%lld", &n, &D);
    for (int i = 1; i <= n; ++i) scanf("%lld", &a[i]);
    build(1, 1, n);
    int q; scanf("%d", &q);
    while (q--) {
        int l, r; scanf("%d%d", &l, &r);
        auto [x, y] = query(1, 1, n, l, r, 1e18);
        if (x < 0) puts("-1");
        else printf("%lld\n", y);
    }
    return 0;
}
posted @   hhoppitree  阅读(71)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示