Solution Set【2024.1.2】

[SDOI2012] 任务安排 / 任务安排

\(f_i\) 表示前 \(i\) 个任务的最小花费,发现转移时需要前一部分分的批数,存在后效性。

考虑在每次分出新的一批任务时计算其对之后所有任务的贡献,有转移:

\[f_i = \min\limits_{j < i}\left\{f_j + st_i \left(sc_i - sc_j\right) + s\left(sc_n - sc_j\right)\right\} \]

其中 \(st_i = \sum\limits_{j \le i} T_j, sc_i = \sum\limits_{j \le i} C_j\)

上式可以化简为:

\[f_i = st_i \times sc_i + s \times sc_n + \min\limits_{j < i}\left\{f_j - s \times sc_j - st_i \times sc_j\right\} \]

斜率优化即可。

注意到每次查询的斜率不存在单调性,在单调栈上二分即可。

时间复杂度 \(O(n \log n)\)

Code
#include <bits/stdc++.h>

typedef long long valueType;
typedef std::vector<valueType> ValueVector;
typedef std::pair<valueType, valueType> ValuePair;
typedef std::vector<ValuePair> PairVector;
typedef long double realType;

bool CompareLess(ValuePair const &A, ValuePair const &B, ValuePair const &C, ValuePair const &D) {
    return (D.second - C.second) * (B.first - A.first) < (B.second - A.second) * (D.first - C.first);
}

bool CompareGreater(ValuePair const &A, ValuePair const &B, ValuePair const &C, ValuePair const &D) {
    return (D.second - C.second) * (B.first - A.first) > (B.second - A.second) * (D.first - C.first);
}

template<typename _Compare>
bool Compare(ValuePair const &A, ValuePair const &B, ValuePair const &C, ValuePair const &D, _Compare const &__compare) {
    return __compare((D.second - C.second) * (B.first - A.first), (B.second - A.second) * (D.first - C.first));
}

bool CompareLess(ValuePair const &A, ValuePair const &B, ValuePair const &C) {
    return CompareLess(A, B, B, C);
}

bool CompareGreater(ValuePair const &A, ValuePair const &B, ValuePair const &C) {
    return CompareGreater(A, B, B, C);
}

template<typename _Compare>
bool Compare(ValuePair const &A, ValuePair const &B, ValuePair const &C, _Compare const &__compare) {
    return Compare(A, B, B, C, __compare);
}

bool CompareLess(ValuePair const &A, ValuePair const &B, valueType const K) {
    return (B.second - A.second) < K * (B.first - A.first);
}

bool CompareGreater(ValuePair const &A, ValuePair const &B, valueType const K) {
    return (B.second - A.second) > K * (B.first - A.first);
}

template<typename _Compare>
bool Compare(ValuePair const &A, ValuePair const &B, valueType const K, _Compare const &__compare) {
    return __compare(B.second - A.second, K * (B.first - A.first));
}

int main() {
    std::ios::sync_with_stdio(false);
    std::cin.tie(nullptr);
    std::cout.tie(nullptr);

    valueType N, S;

    std::cin >> N >> S;

    ValueVector T(N + 1, 0), C(N + 1, 0);

    for (valueType i = 1; i <= N; ++i)
        std::cin >> T[i] >> C[i];

    std::partial_sum(T.begin(), T.end(), T.begin());
    std::partial_sum(C.begin(), C.end(), C.begin());

    ValueVector F(N + 1, 0);

    PairVector Q;

    Q.reserve(N + 1);

    Q.emplace_back(C[0], F[0] - S * C[0]);

    for (valueType i = 1; i <= N; ++i) {
        valueType const K = T[i];

        valueType pointer = (valueType) Q.size() - 1;

        {
            valueType left = 0, right = (valueType) Q.size() - 2;

            while (left <= right) {
                valueType const mid = (left + right) / 2;

                if (Compare(Q[mid], Q[mid + 1], K, std::greater_equal<>())) {
                    right = mid - 1;

                    pointer = mid;
                } else {
                    left = mid + 1;
                }
            }
        }

        auto const &[x, y] = Q[pointer];

        F[i] = y - x * K + T[i] * C[i] + S * C[N];

        ValuePair const Node(C[i], F[i] - S * C[i]);

        while (Q.size() > 1 && Compare(Q[Q.size() - 2], Q[Q.size() - 1], Node, std::less_equal<>()))
            Q.pop_back();

        Q.push_back(Node);
    }

    std::cout << F[N] << std::endl;

    return 0;
}

丝之割

可以发现,若数对 \(\left(u_0, v_0\right)\) 被除去,那么所有满足 \(u \ge u_0\)\(v \le v_0\) 的数对均会被除去。

因此若将所有数对按 \(u\) 升序排列,那么剩余的有用的数对一定满足 \(v\) 严格递增。

这样之后每次消除数对一定是消除一个区间的数对,可以使用动态规划。

\(f_i\) 表示前 \(i\) 个数对的最小花费,那么有转移:

\[f_i = \min\limits_{j < i}\left\{f_j + \min\limits_{k < u_{j + 1}} a_k \times \min\limits_{k > v_{i}} b_k\right\} \]

其中 \(u_i, v_i\) 表示第 \(i\) 个数对的两个数,维护出 \(a\) 的前缀最小值和 \(b\) 的后缀最小值即可。

这样转移式就可以化为斜率优化的形式,使用斜率优化即可。

Code
#include <bits/stdc++.h>

typedef long long valueType;
typedef std::vector<valueType> ValueVector;
typedef std::pair<valueType, valueType> ValuePair;
typedef std::vector<ValuePair> PairVector;
typedef long double realType;

template<typename _Compare>
bool Compare(ValuePair const &A, ValuePair const &B, ValuePair const &C, ValuePair const &D, _Compare const &__compare) {
    return __compare((D.second - C.second) * (B.first - A.first), (B.second - A.second) * (D.first - C.first));
}

template<typename _Compare>
bool Compare(ValuePair const &A, ValuePair const &B, ValuePair const &C, _Compare const &__compare) {
    return Compare(A, B, B, C, __compare);
}

template<typename _Compare>
bool Compare(ValuePair const &A, ValuePair const &B, valueType const K, _Compare const &__compare) {
    return __compare(B.second - A.second, K * (B.first - A.first));
}

constexpr valueType INF = std::numeric_limits<valueType>::max();

int main() {
    std::ios::sync_with_stdio(false);
    std::cin.tie(nullptr);
    std::cout.tie(nullptr);

    valueType N, M;

    std::cin >> N >> M;

    ValueVector A(N + 10, INF), B(N + 10, INF);
    PairVector C(M);

    for (valueType i = 1; i <= N; ++i)
        std::cin >> A[i];

    for (valueType i = 1; i <= N; ++i)
        std::cin >> B[i];

    for (auto &[x, y] : C)
        std::cin >> x >> y;

    std::sort(C.begin(), C.end());

    PairVector D;

    D.reserve(M);

    for (auto const &[x, y] : C) {
        while (!D.empty() && D.back().first == x && D.back().second <= y)
            D.pop_back();

        if (D.empty() || D.back().second < y)
            D.emplace_back(x, y);
    }

    M = (valueType) D.size() - 1;

    ValueVector U(M + 10, N), V(M + 10, N);

    for (valueType i = 0; i <= M; ++i)
        std::tie(U[i], V[i]) = D[i];

    for (valueType i = 1; i <= N; ++i)
        A[i] = std::min(A[i], A[i - 1]);

    for (valueType i = N; i >= 1; --i)
        B[i] = std::min(B[i], B[i + 1]);

    ValueVector F(M + 1, 0);

    PairVector Q;

    Q.reserve(M + 1);

    Q.emplace_back(-A[U[0] - 1], 0);

    valueType pointer = 0;

    for (valueType i = 0; i <= M; ++i) {
        valueType const K = B[V[i] + 1];

        while (pointer + 1 < Q.size() && Compare(Q[pointer], Q[pointer + 1], K, std::less_equal<>()))
            ++pointer;

        auto const &[x, y] = Q[pointer];

        F[i] = y - x * K;

        ValuePair const Node(-A[U[i + 1] - 1], F[i]);

        while (Q.size() > 1 && Compare(Q[Q.size() - 2], Q[Q.size() - 1], Node, std::less_equal<>()))
            Q.pop_back();

        Q.push_back(Node);
    }

    std::cout << F[M] << std::endl;

    return 0;
}

CF1599F Mars

题意可以转化为询问一个区间是否可以重排为一段模 \(P = 10^9 + 7\) 意义下的等差数列。

设等差数列的首项为 \(A\),区间长度为 \(n\),若我们得到区间内数的和 \(S\),那么根据等差数列求和公式:

\[S = n \times A + \dfrac{n\left(n - 1\right)}{2} \times d \]

我们可以得到 \(A\) 的表达式:

\[A = \dfrac{S - \dfrac{n\left(n - 1\right)}{2} \times d}{n} \]

既然已经确定了公差,首项和长度,那么我们可以计算出该等差数列的每项的 \(k\) 次方和,以达到哈希的目的,再与原区间的 \(k\) 次方和比较即可。

考虑如何求出 \(k\) 次方和,可以发现其为:

\[\begin{aligned} \sum\limits_{i = 0}^{n - 1} \left(A + di\right)^k \end{aligned}\]

可以使用二项式定理展开,得到:

\[\begin{aligned} &\sum\limits_{i = 0}^{n - 1}\sum\limits_{j = 0}^{k} \dbinom{k}{j} A_{k - j} \left(di\right)^j \\ =&\sum\limits_{j = 0}^{k} \dbinom{k}{j} A^{k - j} d^j\left(\sum\limits_{i = 0}^{n - 1} i^j\right) \\ \end{aligned}\]

\(\mathcal{O}(k)\) 计算即可。

Code
#include <bits/stdc++.h>

typedef long long valueType;
typedef std::vector<valueType> ValueVector;
typedef std::vector<ValueVector> ValueMatrix;
typedef std::vector<ValueMatrix> ValueCube;

namespace MODINT_WITH_FIXED_MOD {
    constexpr valueType MOD = 1e9 + 7;

    template<typename T1, typename T2, typename T3 = valueType>
    void Inc(T1 &a, T2 b, const T3 &mod = MOD) {
        a = a + b;

        if (a >= mod)
            a -= mod;
    }

    template<typename T1, typename T2, typename T3 = valueType>
    void Dec(T1 &a, T2 b, const T3 &mod = MOD) {
        a = a - b;

        if (a < 0)
            a += mod;
    }

    template<typename T1, typename T2, typename T3 = valueType>
    T1 sum(T1 a, T2 b, const T3 &mod = MOD) {
        return a + b >= mod ? a + b - mod : a + b;
    }

    template<typename T1, typename T2, typename T3 = valueType>
    T1 sub(T1 a, T2 b, const T3 &mod = MOD) {
        return a - b < 0 ? a - b + mod : a - b;
    }

    template<typename T1, typename T2, typename T3 = valueType>
    T1 mul(T1 a, T2 b, const T3 &mod = MOD) {
        return (long long) a * b % mod;
    }

    template<typename T1, typename T2, typename T3 = valueType>
    void Mul(T1 &a, T2 b, const T3 &mod = MOD) {
        a = (long long) a * b % mod;
    }

    template<typename T1, typename T2, typename T3 = valueType>
    T1 pow(T1 a, T2 b, const T3 &mod = MOD) {
        T1 result = 1;

        while (b > 0) {
            if (b & 1)
                Mul(result, a, mod);

            Mul(a, a, mod);
            b = b >> 1;
        }

        return result;
    }
}// namespace MODINT_WITH_FIXED_MOD

using namespace MODINT_WITH_FIXED_MOD;

namespace HASH {
    constexpr static valueType const HASH_COUNT = 8;
    typedef std::array<valueType, HASH_COUNT> HashArray;
    constexpr static HashArray const HASH_BASE = {1, 2, 3, 5, 7, 8, 9, 17};

    struct Hash {
        HashArray data;

        Hash() : data() {
            data.fill(0);
        }

        explicit Hash(valueType x) : data() {
            for (valueType i = 0; i < HASH_COUNT; ++i)
                data[i] = pow(x, HASH_BASE[i], MOD);
        }

        Hash(valueType x, HashArray last) : data() {
            for (valueType i = 0; i < HASH_COUNT; ++i)
                data[i] = sum(last[i], pow(x, HASH_BASE[i], MOD), MOD);
        }

        Hash(valueType x, Hash last) : data() {
            for (valueType i = 0; i < HASH_COUNT; ++i)
                data[i] = sum(last.data[i], pow(x, HASH_BASE[i], MOD), MOD);
        }

        bool operator==(Hash const &other) const {
            return data == other.data;
        }

        bool operator!=(Hash const &other) const {
            return data != other.data;
        }
    };

    ValueMatrix HASH_POWER;
    std::array<ValueMatrix, HASH_COUNT> HASH_COMBINE;


    void initHashPower(valueType len) {
        static constexpr valueType const HASH_MAX_POWER_COUNT = 20;

        HASH_POWER.resize(HASH_MAX_POWER_COUNT, ValueVector(len + 1, 0));

        for (valueType i = 0; i < HASH_MAX_POWER_COUNT; ++i) {
            HASH_POWER[i][0] = i == 0;

            for (valueType j = 1; j <= len; ++j)
                HASH_POWER[i][j] = pow(j, i);

            for (valueType j = 1; j <= len; ++j)
                Inc(HASH_POWER[i][j], HASH_POWER[i][j - 1]);
        }

        /* ======================================================================= */

        for (valueType i = 0; i < HASH_COUNT; ++i) {
            valueType const k = HASH_BASE[i];

            HASH_COMBINE[i].resize(k + 1, ValueVector(k + 1, 0));
            HASH_COMBINE[i][0][0] = 1;

            for (valueType j = 1; j <= k; ++j) {
                HASH_COMBINE[i][j][0] = 1;

                for (valueType l = 1; l <= j; ++l)
                    HASH_COMBINE[i][j][l] = sum(HASH_COMBINE[i][j - 1][l], HASH_COMBINE[i][j - 1][l - 1], MOD);
            }
        }
    }

    typedef std::vector<Hash> HashVector;

    Hash GetHash(valueType n, valueType a, valueType d) {
        Hash result;

        for (valueType i = 0; i < HASH_COUNT; ++i) {
            valueType const k = HASH_BASE[i];

            for (valueType j = 0; j <= k; ++j)
                Inc(result.data[i], mul(mul(HASH_COMBINE[i][k][j], HASH_POWER[j][n - 1]), mul(pow(a, k - j), pow(d, j))));
        }

        return result;
    }

    Hash GetSubHash(valueType l, valueType r, HashVector const &hash) {
        Hash result;

        for (valueType i = 0; i < HASH_COUNT; ++i)
            result.data[i] = sub(hash[r].data[i], hash[l - 1].data[i]);

        return result;
    }

    valueType GetSum(valueType l, valueType r, HashVector const &hash) {
        return sub(hash[r].data[0], hash[l - 1].data[0]);
    }
}// namespace HASH

using namespace HASH;

int main() {
    std::ios::sync_with_stdio(false);
    std::cin.tie(nullptr);
    std::cout.tie(nullptr);

    valueType N, Q;

    std::cin >> N >> Q;

    ValueVector A(N + 1);

    for (valueType i = 1; i <= N; ++i)
        std::cin >> A[i];

    {
        valueType const seed = std::chrono::steady_clock::now().time_since_epoch().count() ^ std::random_device()() ^ (unsigned long long) std::make_unique<char>().get();

        valueType const shifting = std::mt19937(seed)() % MOD;

        for (auto &x : A)
            Inc(x, shifting);
    }

    initHashPower(N);

    HashVector hash(N + 1);

    for (valueType i = 1; i <= N; ++i)
        hash[i] = Hash(A[i], hash[i - 1]);

    for (valueType q = 0; q < Q; ++q) {
        valueType l, r, D;

        std::cin >> l >> r >> D;

        valueType const S = GetSum(l, r, hash), L = r - l + 1;

        valueType const A = sub(mul(S, pow(L, MOD - 2)), mul((MOD + 1) / 2, mul(L - 1, D)));

        if (GetHash(L, A, D) == GetSubHash(l, r, hash))
            std::cout << "Yes\n";
        else
            std::cout << "No\n";
    }

    std::cout << std::flush;

    return 0;
}

CF713C Sonya and Problem Wihtout a Legend / 序列 sequence / CF13C Sequence / [USACO08FEB] Making the Grade G

若限制严格单调递增,可以将 \(a_i\) 均减去 \(i\),这样我们只需要使得 \(a_i\) 单调不降即可。

\(f_{i, j}\) 表示使得前 \(i\) 个数符合要求,且第 \(i\) 个数为 \(j\) 的最小花费,有转移:

\[f_{i, j} = \min\limits_{k \le j}\left\{f_{i - 1, k} + \left\lvert a_i - j \right\rvert\right\} \]

\(g\)\(f\) 的前缀最小值,那么有转移:

\[f_{i, j} = g_{i - 1, k} + \left\lvert a_i - j \right\rvert \]

考虑设 \(F_i(x) = f_{i, x}, G_i(x) = g_{i, x}\),那么可以发现其均为关于 \(x\) 的凸函数,因此可以使用 Slope Trick 进行优化。

Code of CF713C
#include <bits/stdc++.h>

typedef long long valueType;
typedef std::vector<valueType> ValueVector;
typedef std::priority_queue<valueType, ValueVector, std::less<valueType>> ValueMaxHeap;

int main() {
    std::ios::sync_with_stdio(false);
    std::cin.tie(nullptr);
    std::cout.tie(nullptr);

    valueType N;

    std::cin >> N;

    valueType ans = 0;

    ValueMaxHeap Q;

    for (valueType i = 1; i <= N; ++i) {
        valueType x;

        std::cin >> x;

        x -= i;

        Q.push(x);

        if (x < Q.top()) {
            ans += Q.top() - x;

            Q.pop();

            Q.push(x);
        }
    }

    std::cout << ans << std::endl;

    return 0;
}

[ARC123D] Inc, Dec - Decomposition

由于 \(a_i\) 是给定的,因此若确定了 \(b_i\) 那么 \(c_i\) 也就确定了。

进而我们可以将题目限制转化为:

  • \(b_i \le b_{i + 1}\)
  • \(a_i - b_i \ge a_{i + 1} - b_{i + 1} \Leftrightarrow b_i + \left(a_{i + 1} - a_i\right) \le b_{i + 1}\)

\(s_i = \max\left\{0, a_{i + 1} - a_i\right\}\),那么上述条件即为 \(b_i + s_i \le b_{i + 1}\)

这样我们的问题就转化为了:

确定一个整数序列 \(b_i\),使得:

  • \(b_i + s_i \le b_{i + 1}\)
  • 最小化 \(\sum\limits_{i} \left\lvert b_i \right\rvert + \left\lvert a_i - b_i \right\rvert = \sum\limits_{i} \left\lvert b_i \right\rvert + \left\lvert b_i - a_i \right\rvert\)

\(f_{i, x}\) 表示使得前 \(i\) 个数合法且 \(b_i = x\) 的最小花费,考虑如何转移:

由于有 \(b_i + s_i \le b_{i + 1}\) 的限制,那么有:

\[f_{i + 1, x} = \min\limits_{j \le x - s_i} f_{i, j} + \left\lvert x \right\rvert + \left\lvert x - a_i \right\rvert \]

考虑设 \(F_i(x) = f_{i, x}\),下面考虑如何从 \(F_i\) 转移到 \(F_{i + 1}\)。我们有:

\[F_{i + 1}(x) = \min\limits_{y + s_i \le x} F_{i}(y) + \left\lvert x \right\rvert + \left\lvert x - a_i \right\rvert \]

\(G_i(x) = \min\limits_{y \le x} F_i(y)\),那么有:

\[F_{i + 1}(x) = G_{i}(x - s_i) + \left\lvert x \right\rvert + \left\lvert x - a_i \right\rvert \]

使用 Slope Trick 即可。

Code
#include <bits/stdc++.h>

typedef long long valueType;
typedef std::vector<valueType> ValueVector;
typedef std::priority_queue<valueType, ValueVector, std::greater<valueType>> ValueMinHeap;
typedef std::priority_queue<valueType, ValueVector, std::less<valueType>> ValueMaxHeap;

int main() {
    std::ios::sync_with_stdio(false);
    std::cin.tie(nullptr);
    std::cout.tie(nullptr);

    valueType N;

    std::cin >> N;

    ValueVector A(N + 1, 0);

    for (valueType i = 1; i <= N; ++i)
        std::cin >> A[i];

    valueType ans = 0, shifting = 0;

    ValueMaxHeap Q;

    for (valueType i = 1; i <= N; ++i) {
        shifting -= std::max<valueType>(0, A[i] - A[i - 1]);

        Q.push(shifting);
        Q.push(A[i] + shifting);

        ans += Q.top() - shifting;
        ans += Q.top() - (A[i] + shifting);

        Q.pop();
    }

    std::cout << ans << std::endl;
}
posted @ 2024-01-02 21:56  User-Unauthorized  阅读(18)  评论(0编辑  收藏  举报