1 条题解

  • 0
    @ 2026-10-26 14:35:24

    思路与算法

    记S为库存前缀和,T为库存乘位置的前缀和。强制最后仓库在i的最优值f[i]=C[i]+X[i]S[i]-T[i]+min_{j<i}(f[j]+T[j]-X[i]S[j])。把j写成斜率-S[j]、截距f[j]+T[j]的直线,用单调凸包查询。位置递增、库存非负使查询点递增、斜率不增。最终只需最后仓库不早于最后一个有货工厂;零库存尾部可取相应f的最小值,不强制在N另建仓。

    正确性说明

    若前一个仓库是j,j+1至i的货物都送到i,运输和为Xi-(T[i]-T[j]),直接得到递推。每种仓库划分都被枚举,最优子结构成立。单调凸包删去交点次序错误的中间直线及同斜率劣线,未来不会最优;查询点递增时,已被下一线超过的首线也不会再最优。零库存后的工厂可以不建,最终最小化最后有货点之后的所有候选,覆盖完整题意。

    复杂度

    每条线至多入队、出队一次,时间O(N)、空间O(N),输入与队列预留初始化也计入。N≤10^6,使用128位整数作线性表达式、自包含的三段64位整数作192位精确交叉乘积比较,不做浮点斜率;题目保证最终与原题中间费用在long long内,输出转64位。

    易错点

    库存为0时斜率可能相同,要保留较小截距。不能无条件返回f[N]:例0位置有1件且建仓费1、下一厂零库存费100,答案是1。独立小例枚举全部仓库子集;百万工厂统一成本10的下界10+L(L-1)/2≥4L与长度4或5分组给出精确最优4N。

    参考代码(C++11)

    #include <bits/stdc++.h>
    using namespace std;
    using Wide = __int128;
    struct Line {
        long long slope;
        Wide intercept;
    };
    Wide value(Line l, long long x) {
        return Wide(l.slope) * x + l.intercept;
    }
    using UnsignedWide = unsigned __int128;
    struct SignedProduct {
        bool negative;
        unsigned long long high, middle, low;
    };
    SignedProduct multiplyExact(Wide number, long long factor) {
        bool negative = number < 0;
        UnsignedWide magnitude = negative ? UnsignedWide(-(number + 1)) + 1 : UnsignedWide(number);
        unsigned long long f = (unsigned long long)factor;
        UnsignedWide lower = UnsignedWide((unsigned long long)magnitude) * f;
        UnsignedWide upper = UnsignedWide((unsigned long long)(magnitude >> 64)) * f + (lower >> 64);
        return {negative && magnitude != 0 && f != 0, (unsigned long long)(upper >> 64),
                (unsigned long long)upper, (unsigned long long)lower};
    }
    bool productAtLeast(SignedProduct a, SignedProduct b) {
        if (a.negative != b.negative)
            return !a.negative;
        auto x = make_tuple(a.high, a.middle, a.low);
        auto y = make_tuple(b.high, b.middle, b.low);
        return a.negative ? x <= y : x >= y;
    }
    bool obsolete(Line a, Line b, Line c) {
        return productAtLeast(multiplyExact(b.intercept - a.intercept, b.slope - c.slope),
                              multiplyExact(c.intercept - b.intercept, a.slope - b.slope));
    }
    int main() {
        ios::sync_with_stdio(false);
        cin.tie(nullptr);
        int n;
        cin >> n;
        vector<Line> q;
        q.reserve(n + 1);
        q.push_back({0, 0});
        size_t head = 0;
        long long S = 0;
        Wide T = 0, answer = 0;
        for (int i = 0; i < n; ++i) {
            long long x, p, c;
            cin >> x >> p >> c;
            S += p;
            T += Wide(x) * p;
            while (head + 1 < q.size() && value(q[head], x) >= value(q[head + 1], x))
                ++head;
            Wide f = value(q[head], x) + Wide(x) * S - T + c;
            if (p > 0)
                answer = f;
            else
                answer = min(answer, f);
            Line next = {-S, f + T};
            if (q.size() > head && q.back().slope == next.slope) {
                if (q.back().intercept <= next.intercept)
                    continue;
                q.pop_back();
            }
            while (q.size() >= head + 2 && obsolete(q[q.size() - 2], q.back(), next))
                q.pop_back();
            q.push_back(next);
        }
        cout << (long long)answer << '\n';
    }
    
    • 1

    信息

    ID
    1784
    时间
    1000ms
    内存
    512MiB
    难度
    普及+
    标签
    递交数
    1
    已通过
    1
    上传者