Solution Set【2024.1.1】

实际上本文涵盖了 \(2023.12.30 \sim 2024.1.1\) 之间的题目。

[Ynoi2006] rldcot

考虑如下两个点对:

  • \(\operatorname{lca}(x, y) = \operatorname{lca}(a, b) = u\)

  • \(x \le a \le b \le y\)

容易发现,若 \(\left(x, y\right)\) 被选中,则 \(\left(a, b\right)\) 也一定被选中,称之为 \(\left(a, b\right)\) 支配了 \(\left(x, y\right)\)

因此只需要统计出所有未被支配的点对即可。

考虑什么样的点对是未被支配的。

  • \(\operatorname{lca}(x, y) = u\),则 \(\left(x, y\right)\) 未被支配当且仅当不存在 \(x < z < y\)\(\operatorname{lca}(x, z) = \operatorname{lca}(y, z) = u\)

因此我们可以在固定 \(u\) 的情况下枚举 \(x\),在其他兄弟子树的点集中寻找前驱和后继,即可得到所有未被支配的点对。

这一过程可以通过树上启发式合并实现。具体的,维护一个按标号排序的有序点集,每次保留重儿子的点集,将其他点集合并到重儿子的点集中,在合并其他儿子的点集时统计点对。

每个点会被合并 \(\mathcal{O}(\log n)\) 次,每次会贡献 \(\mathcal{O}(1)\) 个点对,因此有效点对数量为 \(\mathcal{O}(n \log n)\),这部分的时间复杂度为 \(\mathcal{O}(n \log^2 n)\)

接下来问题转化为如何统计答案。

首先我们将数对按最近公共祖先的深度值分组,那么询问 \(\left(l, r\right)\) 可以转化为询问有多少组点对存在点对 \(\left(x, y\right)\),满足 \(1 \le l \le x\)\(y \le r \le n\)

因此我们可以将点对 \(\left(x, y\right)\)\(x\) 的值降序排序,然后维护一个树状数组和一个数组,树状数组用于维护答案,数组用于维护某个颜色 \(c\) 对应的出现过的最小 \(y\) 值,记为 \(last_c\)。那么每次插入一个点对 \(\left(x, y\right)\) 时,若 \(y < last_c\),那么就在树状数组上对区间 \(\left[y, last_c\right)\) 进行区间加操作,然后将 \(last_x\) 更新为 \(y\)。处理询问时单点查询即可。

总复杂度为 \(\mathcal{O}(n \log^2 n)\)

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

typedef long long valueType;
typedef std::vector<valueType> ValueVector;
typedef std::vector<ValueVector> ValueMatrix;
typedef std::set<valueType> ValueSet;
typedef std::tuple<valueType, valueType, valueType> ValueTuple;
typedef std::vector<ValueTuple> TupleVector;
typedef std::vector<TupleVector> TupleMatrix;
typedef std::pair<valueType, valueType> ValuePair;
typedef std::vector<ValuePair> PairVector;
typedef std::vector<PairVector> PairMatrix;

template<typename T, class Operator = std::plus<>>
class TreeArray {
private:
    T N;
    std::vector<T> tree;
    Operator op;

    static T lowbit(T x) {
        return x & (-x);
    }

public:
    TreeArray() = default;

    explicit TreeArray(T n, T initValue = 0) : N(n), tree(n + 1, initValue) {}

    void insert(T pos, T value) {
#ifdef _UU_DEBUG
        if (pos <= 0 || pos > N)
            throw std::out_of_range("TreeArray::insert() : pos out of range");
#endif

        while (pos <= N) {
            tree[pos] = op(tree[pos], value);

            pos += lowbit(pos);
        }
    }

    T query(T pos) {
#ifdef _UU_DEBUG
        if (pos <= 0 || pos > N)
            throw std::out_of_range("TreeArray::query() : pos out of range");
#endif

        T result = 0;

        while (pos > 0) {
            result = op(result, tree[pos]);

            pos -= lowbit(pos);
        }

        return result;
    }
};

valueType N, M;
PairMatrix G;
TupleVector pairs;
ValueSet S;
ValueVector dist, size, son, leftBound, rightBound, node;

void dfs(valueType x, valueType from) {
    static valueType dfsCount = 0;

    size[x] = 1;
    son[x] = 0;
    leftBound[x] = ++dfsCount;
    node[dfsCount] = x;

    for (auto const &[to, weight] : G[x]) {
        if (to == from)
            continue;

        dist[to] = dist[x] + weight;

        dfs(to, x);

        size[x] += size[to];

        if (son[x] == 0 || size[to] > size[son[x]])
            son[x] = to;
    }

    rightBound[x] = dfsCount;
}

void calc(valueType x, valueType from, bool keep) {
    for (auto const &[to, weight] : G[x])
        if (to != son[x] && to != from)
            calc(to, x, false);

    if (son[x] != 0)
        calc(son[x], x, true);

    pairs.emplace_back(x, x, dist[x]);
    S.insert(x);

    for (auto const &[to, weight] : G[x]) {
        if (to == from || to == son[x])
            continue;

        for (valueType i = leftBound[to]; i <= rightBound[to]; ++i) {
            valueType y = node[i];

            auto iter = S.upper_bound(y);

            if (iter != S.end())
                pairs.emplace_back(y, *iter, dist[x]);

            if (iter != S.begin())
                pairs.emplace_back(*std::prev(iter), y, dist[x]);
        }

        for (valueType i = leftBound[to]; i <= rightBound[to]; ++i)
            S.insert(node[i]);
    }

    if (!keep)
        S.clear();
}

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

    std::cin >> N >> M;

    G.resize(N + 1);
    dist.resize(N + 1, 0);
    size.resize(N + 1);
    son.resize(N + 1);
    leftBound.resize(N + 1);
    rightBound.resize(N + 1);
    node.resize(N + 1);
    pairs.reserve(2 * N * std::__lg(N));

    for (valueType i = 1; i < N; ++i) {
        valueType u, v, w;

        std::cin >> u >> v >> w;

        G[u].emplace_back(v, w);
        G[v].emplace_back(u, w);
    }

    dfs(1, 0);

    ValueVector bucket(dist.begin() + 1, dist.end());
    bucket.push_back(std::numeric_limits<valueType>::min());
    std::sort(bucket.begin(), bucket.end());
    bucket.erase(std::unique(bucket.begin(), bucket.end()), bucket.end());
    for (auto &d : dist)
        d = std::distance(bucket.begin(), std::lower_bound(bucket.begin(), bucket.end(), d));

    calc(1, 0, true);

    PairMatrix nodes(N + 1), query(N + 1);

    for (auto const &[l, r, c] : pairs)
        nodes[l].emplace_back(r, c);

    for (valueType i = 0; i < M; ++i) {
        valueType l, r;

        std::cin >> l >> r;

        query[l].emplace_back(r, i);
    }

    TreeArray<valueType> tree(N + 5);
    ValueVector ans(M);
    ValueVector last(N + 1, N + 1);

    for (valueType l = N; l >= 1; --l) {
        for (auto const &[r, c] : nodes[l]) {
            if (r >= last[c])
                continue;

            tree.insert(r, 1);
            tree.insert(last[c], -1);

            last[c] = r;
        }

        for (auto const &[r, id] : query[l])
            ans[id] = tree.query(r);
    }

    std::copy(ans.begin(), ans.end(), std::ostream_iterator<valueType>(std::cout, "\n"));

    return 0;
}

「雅礼集训 2017 Day7」事情的相似度

首先可以通过对字符串建出后缀自动机后将问题放到 \(\tt{parent}\) 树上。这样问题就转化为了给定一个区间,求标号在这个区间内的点对的最近公共祖先的点权最大值。

类似与 [Ynoi2006] rldcot,统计出有效点对后扫描线使用线段树维护最大值即可。

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

typedef long long valueType;
typedef std::vector<valueType> ValueVector;
typedef std::vector<ValueVector> ValueMatrix;
typedef std::string string;
typedef std::vector<bool> bitset;
typedef std::pair<valueType, valueType> ValuePair;
typedef std::vector<ValuePair> PairVector;
typedef std::vector<PairVector> PairMatrix;
typedef std::tuple<valueType, valueType, valueType> ValueTuple;
typedef std::vector<ValueTuple> TupleVector;
typedef std::set<valueType> ValueSet;

template<typename T, class _Compare = std::less<>>
class MinMaxSegmentTree {
private:
    T N;
    std::vector<T> data, lazy;
    _Compare Compare;

    static constexpr T const INIT_VALUE = _Compare()(1, 2) ? std::numeric_limits<T>::max() : std::numeric_limits<T>::min();

    T merge(T const &a, T const &b) {
        return Compare(a, b) ? a : b;
    }

    void merge(T id) {
        data[id] = merge(data[id << 1], data[id << 1 | 1]);
    }

    void push(T id) {
        data[id << 1] = merge(data[id << 1], lazy[id]);
        data[id << 1 | 1] = merge(data[id << 1 | 1], lazy[id]);
        lazy[id << 1] = merge(lazy[id << 1], lazy[id]);
        lazy[id << 1 | 1] = merge(lazy[id << 1 | 1], lazy[id]);

        lazy[id] = INIT_VALUE;
    }

public:
    MinMaxSegmentTree() = default;

    MinMaxSegmentTree(T n) : N(n), data(n * 4 + 10, _Compare()(1, 2) ? std::numeric_limits<T>::max() : std::numeric_limits<T>::min()), lazy(n * 4 + 10, _Compare()(1, 2) ? std::numeric_limits<T>::max() : std::numeric_limits<T>::min()) {}

    MinMaxSegmentTree(T n, T initValue) : N(n), data(n * 4 + 10, initValue), lazy(n * 4 + 10, _Compare()(1, 2) ? std::numeric_limits<T>::max() : std::numeric_limits<T>::min()) {}

    explicit MinMaxSegmentTree(T n, std::vector<T> const &source) : N(n), data(n * 4 + 10), lazy(n * 4 + 10, _Compare()(1, 2) ? std::numeric_limits<T>::max() : std::numeric_limits<T>::min()) {
        build(source, 1, 1, N);
    }

private:
    void build(T id, T l, T r, std::vector<T> const &source) {
        if (l == r) {
            data[id] = source[l];

            return;
        }

        valueType const mid = (l + r) >> 1;

        build(id << 1, l, mid, source);
        build(id << 1 | 1, mid + 1, r, source);

        merge(id);
    }

public:
    void update(T l, T r, T value) {
        update(1, 1, N, l, r, value);
    }

    void update(T pos, T value) {
        update(1, 1, N, pos, pos, value);
    }

    T query(T l, T r) {
        return query(1, 1, N, l, r);
    }

    T query(T pos) {
        return query(1, 1, N, pos, pos);
    }

private:
    void update(T id, T nodeL, T nodeR, T queryL, T queryR, T value) {
        if (queryL <= nodeL && nodeR <= queryR) {
            data[id] = merge(data[id], value);
            lazy[id] = merge(lazy[id], value);

            return;
        }

        push(id);

        valueType const mid = (nodeL + nodeR) >> 1;

        if (queryL <= mid)
            update(id << 1, nodeL, mid, queryL, queryR, value);

        if (queryR > mid)
            update(id << 1 | 1, mid + 1, nodeR, queryL, queryR, value);

        merge(id);
    }

    T query(T id, T nodeL, T nodeR, T queryL, T queryR) {
        if (queryL <= nodeL && nodeR <= queryR)
            return data[id];

        push(id);

        valueType const mid = (nodeL + nodeR) >> 1;

        if (queryR <= mid)
            return query(id << 1, nodeL, mid, queryL, queryR);

        if (queryL > mid)
            return query(id << 1 | 1, mid + 1, nodeR, queryL, queryR);

        return merge(query(id << 1, nodeL, mid, queryL, queryR), query(id << 1 | 1, mid + 1, nodeR, queryL, queryR));
    }
};

template<valueType CharSetSize, valueType BaseChar>
class SuffixAutomaton {
public:
    typedef std::array<valueType, CharSetSize> TransferMap;
    typedef std::vector<TransferMap> TransferMatrix;

private:
    // main data
    valueType N, UsedPoolSize;
    ValueVector Link, Length;
    TransferMatrix Transfer;
    bitset Cloned;

    // data for function extend
    valueType Last;

    // maybe useful
    ValueVector TopoOrder;
    ValueVector EndPosCount;

public:
    SuffixAutomaton() = default;

    explicit SuffixAutomaton(valueType n) : N(n), UsedPoolSize(N), Link(2 * N + 1), Length(2 * N + 1), Transfer(2 * N + 1), Cloned(2 * N + 1, false), Last(0) {
        Link[0] = -1;

        for (auto &p : Transfer)
            p.fill(0);
    }

    explicit SuffixAutomaton(string const &s) : SuffixAutomaton(s.size()) {
        for (size_t i = 0; i < s.size(); ++i)
            extend(s[i] - BaseChar, i + 1);
    }

    explicit SuffixAutomaton(ValueVector const &s) : SuffixAutomaton(s.size()) {
        for (size_t i = 0; i < s.size(); ++i)
            extend(s[i], i + 1);
    }

    void extend(valueType ch, valueType New) {
        Length[New] = Length[Last] + 1;

        valueType P = Last;

        Last = New;

        while (P != -1 && !Transfer[P][ch]) {
            Transfer[P][ch] = New;

            P = Link[P];
        }

        if (P == -1) {
            Link[New] = 0;
        } else {
            valueType Q = Transfer[P][ch];

            if (Length[P] + 1 == Length[Q]) {
                Link[New] = Q;
            } else {
                valueType Clone = ++UsedPoolSize;

                Cloned[Clone] = true;

                Length[Clone] = Length[P] + 1;
                Link[Clone] = Link[Q];
                Transfer[Clone] = Transfer[Q];

                while (P != -1 && Transfer[P][ch] == Q) {
                    Transfer[P][ch] = Clone;

                    P = Link[P];
                }

                Link[Q] = Link[New] = Clone;
            }
        }
    }

public:
    // extra functions
    void GetTopoOrder() {
        TopoOrder.resize(UsedPoolSize + 1);

        ValueVector Count(N + 1, 0);

        for (valueType i = 0; i <= UsedPoolSize; ++i)
            ++Count[Length[i]];

        for (valueType i = 1; i <= N; ++i)
            Count[i] += Count[i - 1];

        for (valueType i = 0; i <= UsedPoolSize; ++i)
            TopoOrder[--Count[Length[i]]] = i;

        std::reverse(TopoOrder.begin(), TopoOrder.end());
    }

    void GetEndPosCount() {
        EndPosCount.resize(UsedPoolSize + 1);

        std::fill(EndPosCount.begin(), EndPosCount.end(), 0);

        for (valueType i = 0; i <= UsedPoolSize; ++i)
            if (!Cloned[i])
                EndPosCount[i] = 1;

        for (auto const &u : TopoOrder)
            if (Link[u] != -1)
                EndPosCount[Link[u]] += EndPosCount[u];

        EndPosCount[0] = 0;
    }

    valueType size() const {
        return UsedPoolSize + 1;
    }

    valueType GetLength(valueType u) const {
        return Length[u];
    }

    valueType GetLink(valueType u) const {
        return Link[u];
    }
};

namespace TREE {
    valueType N, M;
    ValueMatrix G;
    TupleVector pairs;
    ValueSet S;
    ValueVector weight, size, son, leftBound, rightBound, node;

    void dfs(valueType x, valueType from) {
        static valueType dfsCount = 0;

        size[x] = 1;
        son[x] = 0;
        leftBound[x] = ++dfsCount;
        node[dfsCount] = x;

        for (auto const &to : G[x]) {
            if (to == from)
                continue;

            dfs(to, x);

            size[x] += size[to];

            if (son[x] == 0 || size[to] > size[son[x]])
                son[x] = to;
        }

        rightBound[x] = dfsCount;
    }

    void calc(valueType x, valueType from, bool keep) {
        for (auto const &to : G[x])
            if (to != son[x] && to != from)
                calc(to, x, false);

        if (son[x] != 0)
            calc(son[x], x, true);

        {
            auto iter = S.upper_bound(x);

            if (iter != S.end())
                pairs.emplace_back(x, *iter, weight[x]);

            if (iter != S.begin())
                pairs.emplace_back(*std::prev(iter), x, weight[x]);
            S.insert(x);
        }

        for (auto const &to : G[x]) {
            if (to == from || to == son[x])
                continue;

            for (valueType i = leftBound[to]; i <= rightBound[to]; ++i) {
                valueType y = node[i];

                auto iter = S.upper_bound(y);

                if (iter != S.end())
                    pairs.emplace_back(y, *iter, weight[x]);

                if (iter != S.begin())
                    pairs.emplace_back(*std::prev(iter), y, weight[x]);
            }

            for (valueType i = leftBound[to]; i <= rightBound[to]; ++i)
                S.insert(node[i]);
        }

        if (!keep)
            S.clear();
    }

    void init(valueType n, valueType m) {
        N = n;
        M = m;

        G.resize(N + 10);
        weight.resize(N + 10);
        size.resize(N + 10);
        son.resize(N + 10);
        leftBound.resize(N + 10);
        rightBound.resize(N + 10);
        node.resize(N + 10);
        pairs.reserve(N * std::__lg(N));
    }

    void addEdge(valueType u, valueType v) {
        G[u].emplace_back(v);
        G[v].emplace_back(u);
    }

    void setWeight(valueType u, valueType w) {
        weight[u] = w;
    }

    void solve() {
        dfs(1, 0);
        calc(1, 0, true);

        PairMatrix nodes(N + 1), query(N + 1);

        for (auto const &[l, r, c] : pairs)
            nodes[l].emplace_back(r, c);

        for (valueType i = 0; i < M; ++i) {
            valueType l, r;

            std::cin >> l >> r;

            ++l;
            ++r;

            query[l].emplace_back(r, i);
        }

        MinMaxSegmentTree<valueType, std::greater<>> tree(N + 5);
        ValueVector ans(M);

        for (valueType l = N; l >= 1; --l) {
            for (auto const &[r, c] : nodes[l])
                tree.update(r, N, c);

            for (auto const &[r, id] : query[l])
                ans[id] = tree.query(1, r);
        }

        for (auto &x : ans)
            if (x < 0)
                x = 0;

        std::copy(ans.begin(), ans.end(), std::ostream_iterator<valueType>(std::cout, "\n"));
    }
}// namespace TREE

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

    valueType N, M;

    std::cin >> N >> M;

    std::string S;

    std::cin >> S;

    SuffixAutomaton<2, '0'> SAM(S);

    TREE::init(SAM.size(), M);

    for (valueType i = 0; i < SAM.size(); ++i) {
        TREE::setWeight(i + 1, SAM.GetLength(i));

        if (SAM.GetLink(i) != -1)
            TREE::addEdge(SAM.GetLink(i) + 1, i + 1);
    }

    TREE::solve();

    return 0;
}

LOJ #6198. 谢特

首先对字符串建出后缀自动机后将问题放到 \(\tt{parent}\) 树上。

接下来问题便为点对贡献问题,对于点对间异或值的最大值可以使用 \(\tt{0-1 \, Trie}\) 解决。而最近公共祖先的权值贡献使用树上启发式合并即可。

也可以使用 \(\tt{Trie}\) 合并来在遍历树的过程中维护答案。

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

typedef long long valueType;
typedef std::vector<valueType> ValueVector;
typedef std::vector<ValueVector> ValueMatrix;
typedef std::string string;
typedef std::vector<bool> bitset;
typedef std::pair<valueType, valueType> ValuePair;
typedef std::vector<ValuePair> PairVector;
typedef std::vector<PairVector> PairMatrix;
typedef std::tuple<valueType, valueType, valueType> ValueTuple;
typedef std::vector<ValueTuple> TupleVector;
typedef std::set<valueType> ValueSet;

template<typename T, T MAXBIT>
class BinaryTrie {
private:
    typedef std::array<T, 2> TransferArray;
    typedef std::vector<TransferArray> TransferMatrix;

    T UsedPoolSize, InsertCount;
    TransferMatrix Transfer;
    std::vector<T> count, sum;

    void update(T current) {
        count[current] = 0;
        sum[current] = 0;

        if (Transfer[current][0] != 0) {
            count[current] += count[Transfer[current][0]];
            sum[current] ^= sum[Transfer[current][0]] << 1;
        }

        if (Transfer[current][1] != 0) {
            count[current] += count[Transfer[current][1]];

            sum[current] ^= sum[Transfer[current][1]] << 1 | (count[Transfer[current][1]] & 1);
        }
    }

public:
    BinaryTrie() = default;

    BinaryTrie(T N) : UsedPoolSize(0), InsertCount(0), Transfer((N + 100) * MAXBIT), count((N + 100) * MAXBIT, 0), sum((N + 100) * MAXBIT, 0) {
        for (auto &p : Transfer)
            p.fill(0);
    }

public:
    void insert(T x) {
        ++InsertCount;

        T current = 0;

        for (T i = MAXBIT - 1; i >= 0; --i) {
            T bit = (x >> i) & 1;

            if (Transfer[current][bit] == 0)
                Transfer[current][bit] = ++UsedPoolSize;

            ++count[current];
            sum[current] ^= bit << i;

            current = Transfer[current][bit];
        }

        ++count[current];
        sum[current] ^= x & 1;
    }

    void erase(T x) {
        --InsertCount;

        T current = 0;

        for (T i = MAXBIT - 1; i >= 0; --i) {
            T bit = (x >> i) & 1;

            --count[current];
            sum[current] ^= bit << i;

            current = Transfer[current][bit];
        }
    }

    T GetMax(T x) {
        T current = 0, result = 0;

        for (T i = MAXBIT - 1; i >= 0; --i) {
            T const bit = (x >> i) & 1;

            if (Transfer[current][bit ^ 1] != 0 && count[Transfer[current][bit ^ 1]] != 0) {
                result |= 1 << i;
                current = Transfer[current][bit ^ 1];
            } else {
                current = Transfer[current][bit];
            }
        }

        return result;
    }

    void clear() {
        std::fill(count.begin(), std::next(count.begin(), UsedPoolSize + 10), 0);
        std::fill(sum.begin(), std::next(sum.begin(), UsedPoolSize + 10), 0);

        for (T i = 0; i <= UsedPoolSize; ++i)
            Transfer[i].fill(0);

        UsedPoolSize = 0;
        InsertCount = 0;
    }

    T size() const {
        return InsertCount;
    }

    bool empty() const {
        return InsertCount == 0;
    }
};

typedef BinaryTrie<valueType, 18> Trie;

template<valueType CharSetSize, valueType BaseChar>
class SuffixAutomaton {
public:
    typedef std::array<valueType, CharSetSize> TransferMap;
    typedef std::vector<TransferMap> TransferMatrix;

private:
    // main data
    valueType N, UsedPoolSize;
    ValueVector Link, Length;
    TransferMatrix Transfer;
    bitset Cloned;
    ValueVector FirstPos;

    // data for function extend
    valueType Last;

    // maybe useful
    ValueVector TopoOrder;
    ValueVector EndPosCount;

public:
    SuffixAutomaton() = default;

    explicit SuffixAutomaton(valueType n) : N(n), UsedPoolSize(0), Link(2 * N + 1), Length(2 * N + 1), Transfer(2 * N + 1), Cloned(2 * N + 1, false), FirstPos(2 * N + 1, 0), Last(0) {
        Link[0] = -1;

        for (auto &p : Transfer)
            p.fill(0);
    }

    explicit SuffixAutomaton(string const &s) : SuffixAutomaton(s.size()) {
        for (size_t i = 0; i < s.size(); ++i)
            extend(s[i] - BaseChar, i + 1);
    }

    explicit SuffixAutomaton(ValueVector const &s) : SuffixAutomaton(s.size()) {
        for (size_t i = 0; i < s.size(); ++i)
            extend(s[i], i + 1);
    }

    void extend(valueType ch, valueType pos) {
        valueType New = ++UsedPoolSize;

        Length[New] = Length[Last] + 1;
        FirstPos[New] = pos;

        valueType P = Last;

        Last = New;

        while (P != -1 && !Transfer[P][ch]) {
            Transfer[P][ch] = New;

            P = Link[P];
        }

        if (P == -1) {
            Link[New] = 0;
        } else {
            valueType Q = Transfer[P][ch];

            if (Length[P] + 1 == Length[Q]) {
                Link[New] = Q;
            } else {
                valueType Clone = ++UsedPoolSize;

                Cloned[Clone] = true;

                Length[Clone] = Length[P] + 1;
                Link[Clone] = Link[Q];
                Transfer[Clone] = Transfer[Q];
                FirstPos[Clone] = FirstPos[Q];

                while (P != -1 && Transfer[P][ch] == Q) {
                    Transfer[P][ch] = Clone;

                    P = Link[P];
                }

                Link[Q] = Link[New] = Clone;
            }
        }
    }

public:
    // extra functions
    void GetTopoOrder() {
        TopoOrder.resize(UsedPoolSize + 1);

        ValueVector Count(N + 1, 0);

        for (valueType i = 0; i <= UsedPoolSize; ++i)
            ++Count[Length[i]];

        for (valueType i = 1; i <= N; ++i)
            Count[i] += Count[i - 1];

        for (valueType i = 0; i <= UsedPoolSize; ++i)
            TopoOrder[--Count[Length[i]]] = i;

        std::reverse(TopoOrder.begin(), TopoOrder.end());
    }

    void GetEndPosCount() {
        EndPosCount.resize(UsedPoolSize + 1);

        std::fill(EndPosCount.begin(), EndPosCount.end(), 0);

        for (valueType i = 0; i <= UsedPoolSize; ++i)
            if (!Cloned[i])
                EndPosCount[i] = 1;

        for (auto const &u : TopoOrder)
            if (Link[u] != -1)
                EndPosCount[Link[u]] += EndPosCount[u];

        EndPosCount[0] = 0;
    }

    valueType size() const {
        return UsedPoolSize + 1;
    }

    valueType GetLength(valueType u) const {
        return Length[u];
    }

    valueType GetLink(valueType u) const {
        return Link[u];
    }

    valueType GetFirstPos(valueType u) const {
        return FirstPos[u];
    }
};

class Tree {
private:
    valueType N;
    ValueMatrix G;
    ValueVector weight, size, son, length;
    ValueVector leftBound, rightBound, node;
    Trie trie;

public:
    Tree() = default;

    explicit Tree(valueType n) : N(n), G(n + 1), weight(n + 1), size(n + 1), son(n + 1), length(n + 1), leftBound(n + 1), rightBound(n + 1), node(n + 1), trie(n + 1) {}

public:
    void addEdge(valueType u, valueType v) {
        G[u].push_back(v);
        G[v].push_back(u);
    }

    void setWeight(valueType u, valueType w) {
        weight[u] = w;
    }

    void setLength(valueType u, valueType l) {
        length[u] = l;
    }

    valueType solve() {
        valueType dfsCount = 0;

        dfs(1, 0, dfsCount);

        valueType ans = 0;

        calc(1, 0, true, ans);

        return ans;
    }

private:
    void dfs(valueType x, valueType from, valueType &dfsCount) {
        size[x] = 1;
        son[x] = 0;
        leftBound[x] = ++dfsCount;
        node[dfsCount] = x;

        for (auto const &to : G[x]) {
            if (to == from)
                continue;

            dfs(to, x, dfsCount);

            size[x] += size[to];

            if (son[x] == 0 || size[to] > size[son[x]])
                son[x] = to;
        }

        rightBound[x] = dfsCount;
    }

    void calc(valueType x, valueType from, bool keep, valueType &ans) {
        for (auto const &to : G[x]) {
            if (to == from || to == son[x])
                continue;

            calc(to, x, false, ans);
        }

        if (son[x] != 0)
            calc(son[x], x, true, ans);

        if (!trie.empty() && x != 1)
            ans = std::max(ans, length[x] + trie.GetMax(weight[x]));

        if (x != 1)
            trie.insert(weight[x]);

        for (auto const &to : G[x]) {
            if (to == from || to == son[x])
                continue;

            for (valueType i = leftBound[to]; i <= rightBound[to]; ++i) {
                valueType y = node[i];

                if (!trie.empty())
                    ans = std::max(ans, length[x] + trie.GetMax(weight[y]));
            }

            for (valueType i = leftBound[to]; i <= rightBound[to]; ++i) {
                valueType y = node[i];

                trie.insert(weight[y]);
            }
        }

        if (!keep)
            trie.clear();
    }
};

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

    valueType N;

    std::cin >> N;

    string S;

    std::cin >> S;

    std::reverse(S.begin(), S.end());

    ValueVector weight(N + 1);

    for (valueType i = N; i >= 1; --i)
        std::cin >> weight[i];

    SuffixAutomaton<26, 'a'> SAM(S);

    Tree tree(SAM.size());

    for (valueType i = 1; i < SAM.size(); ++i)
        tree.addEdge(SAM.GetLink(i) + 1, i + 1);

    for (valueType i = 0; i < SAM.size(); ++i)
        tree.setWeight(i + 1, weight[SAM.GetFirstPos(i)]);

    for (valueType i = 0; i < SAM.size(); ++i)
        tree.setLength(i + 1, SAM.GetLength(i));

    std::cout << tree.solve() << std::endl;

    return 0;
}

「Cfz Round 3」Circle

首先可以发现给出的函数实质上是在连边 \(i \rightarrow p_i\) 的图中走 \(k\) 步到达的点的标号。

由于 \(p\) 是一个排列,所以这个图是由若干个环组成的。

考虑有解的充要条件,即对于所有 \(S_i = 1\)\(i\),其所在的环的长度 \(k\) 都满足 \(k \mid l\)。由于对于 \(S_i = 0\)\(i\) 无限制,所以我们统计出所有 \(S_i = 1\)\(i\) 的个数 \(C\),这样我们的问题就转化为了:

给定 \(l, C, n\),构造一个可重集,使得其元素均为 \(l\) 的因子,且元素和在 \(\left[C, n - 2\right] \cup \left\{n\right\}\) 之间。

元素和不能为 \(n - 1\) 是因为这样会剩余一个长度为 \(1\) 的环,而题目中限制了 \(p_i \neq i\),因此这样的环是不合法的。

可以发现,若其元素为合数,那么一定可以通过若干质因子构造出来,因此我们可以枚举质因子,然后判断其是否可以存在在集合中。那么可以发现可以在集合中的数只有 \(\mathcal{O}(\log l)\) 种,因此我们可以进行完全背包,进而可以得到方案,时间复杂度为 \(\mathcal{O}(n \log l)\),可以通过。

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

typedef long long valueType;
typedef std::vector<valueType> ValueVector;
typedef std::vector<ValueVector> ValueMatrix;
typedef std::vector<bool> bitset;
typedef std::set<valueType> ValueSet;
typedef std::map<valueType, valueType> ValueMap;

class LineSieve {
public:
    typedef LineSieve self;
    typedef long long valueType;
    typedef std::vector<valueType> container;

private:
    valueType size;
    container minFactorList;
    container primeList;

public:
    explicit LineSieve(valueType _size_) : size(_size_), minFactorList(_size_ + 1) {
        for (valueType i = 2; i <= size; ++i) {
            if (minFactorList[i] == 0) {
                primeList.push_back(i);
                minFactorList[i] = i;
            }

            for (auto const &iter : primeList) {
                valueType const t = i * iter;

                if (t > size)
                    break;

                minFactorList[t] = iter;

                if (i % iter == 0)
                    break;
            }
        }
    }

    valueType minFactor(valueType x) const {
        if (x > size)
            throw std::range_error("Larger than Size.");

        if (x < 1)
            throw std::range_error("Too small.");

        return minFactorList[x];
    }

    bool isPrime(valueType x) const {
        if (x > size)
            throw std::range_error("Larger than Size.");

        if (x < 1)
            throw std::range_error("Too small.");

        return minFactorList[x] == x;
    }

    valueType maxFactor(valueType x) const {
        if (x > size)
            throw std::range_error("Larger than Size.");

        if (x < 1)
            throw std::range_error("Too small.");

        return isPrime(x) ? x : x / minFactorList[x];
    }

    const container &getPrime() const {
        return primeList;
    }
};

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

    valueType T;

    std::cin >> T;

    for (valueType testcase = 0; testcase < T; ++testcase) {
        valueType N, L;

        std::cin >> N >> L;

        valueType C = 0;

        bitset limit(N + 1, false);

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

            std::cin >> c;

            if (c == '1') {
                ++C;

                limit[i] = true;
            }
        }

        if (L == 0 || L == 1) {
            if (C > 0 && L == 1) {
                std::cout << -1 << '\n';

                continue;
            } else {
                for (valueType i = 1; i <= N; ++i)
                    std::cout << (i % N + 1) << ' ';

                std::cout << '\n';

                continue;
            }

            continue;
        }

        ValueVector const prime = LineSieve(N).getPrime();

        ValueVector factor;

        for (auto const &iter : prime) {
            if (L % iter == 0)
                factor.push_back(iter);
        }

        bitset F(N + 1, false);
        ValueVector path(N + 1, 0);

        F[0] = true;

        for (auto const &x : factor) {
            for (valueType i = x; i <= N; ++i) {
                if (F[i - x]) {
                    F[i] = true;

                    path[i] = x;
                }
            }
        }

        ValueVector pool;

        valueType ans = -1;

        for (valueType i = N; i >= C; --i) {
            if (F[i] && i != N - 1) {
                pool.emplace_back(i);
            }
        }

        if (pool.empty()) {
            std::cout << -1 << '\n';

            continue;
        }

        std::shuffle(pool.begin(), pool.end(), std::mt19937(std::random_device()()));

        ans = pool[0];

        ValueVector B;

        for (valueType i = ans; i > 0; i -= path[i])
            B.push_back(path[i]);

        if (N != ans)
            B.push_back(N - ans);

        ValueVector queue;

        for (valueType i = 1; i <= N; ++i)
            if (limit[i])
                queue.push_back(i);

        for (valueType i = 1; i <= N; ++i)
            if (!limit[i])
                queue.push_back(i);

        valueType pointer = 0;

        ValueVector P(N + 1, 0);

        for (auto const size : B) {
            if (size == 0)
                continue;

            ValueVector temp(queue.begin() + pointer, queue.begin() + pointer + size);

            for (valueType i = 0; i + 1 < size; ++i)
                P[temp[i]] = temp[i + 1];

            P[temp[size - 1]] = temp[0];

            pointer += size;
        }

        for (valueType i = 1; i <= N; ++i)
            std::cout << P[i] << ' ';

        std::cout << '\n';
    }

    std::cout << std::flush;

    return 0;
}

[LNOI2022] 串

可以发现,将 \(T_0\) 设为空串,\(T_1\)\(S_{1 \dots 1}\),之后每次将左右端点 \(l, r\) 执行 \(l \leftarrow l + 1, r \leftarrow r + 2\) 便可以得到一个 \(\left\lfloor\dfrac{N}{2}\right\rfloor\) 的构造,进而确定了答案下界。

考虑如何优化答案,发现若一个子串在 \(S\) 中出现两次,设其第一次出现右端点为 \(r\),长度为 \(len\),那么可以得到一个 \(len + \left\lfloor\dfrac{N - r}{2}\right\rfloor\) 的构造,对所有出现多次的子串求出最大值即可,这一部分可以使用后缀自动机实现。

同时我们只需要考虑最后一次跳转,因为在最后一次跳转前的过程可以认为每次字符串的长度均会加一,所以在这个过程中无论如何跳转都不会影响答案。

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

typedef long long valueType;
typedef std::vector<valueType> ValueVector;
typedef std::vector<bool> bitset;
typedef std::string string;

template<valueType CharSetSize, valueType BaseChar>
class SuffixAutomaton {
public:
    typedef std::array<valueType, CharSetSize> TransferMap;
    typedef std::vector<TransferMap> TransferMatrix;

private:
    // main data
    valueType N, UsedPoolSize;
    ValueVector Link, Length;
    TransferMatrix Transfer;
    bitset Cloned;
    ValueVector FirstPos;

    // data for function extend
    valueType Last;

    // maybe useful
    ValueVector TopoOrder;
    ValueVector EndPosCount;

public:
    SuffixAutomaton() = default;

    explicit SuffixAutomaton(valueType n) : N(n), UsedPoolSize(0), Link(2 * N + 1), Length(2 * N + 1), Transfer(2 * N + 1), Cloned(2 * N + 1, false), FirstPos(2 * N + 1, 0), Last(0) {
        Link[0] = -1;

        for (auto &p : Transfer)
            p.fill(0);
    }

    explicit SuffixAutomaton(string const &s) : SuffixAutomaton(s.size()) {
        for (size_t i = 0; i < s.size(); ++i)
            extend(s[i] - BaseChar, i + 1);
    }

    explicit SuffixAutomaton(ValueVector const &s) : SuffixAutomaton(s.size()) {
        for (size_t i = 0; i < s.size(); ++i)
            extend(s[i], i + 1);
    }

    void extend(valueType ch, valueType pos) {
        valueType New = ++UsedPoolSize;

        Length[New] = Length[Last] + 1;
        FirstPos[New] = pos;

        valueType P = Last;

        Last = New;

        while (P != -1 && !Transfer[P][ch]) {
            Transfer[P][ch] = New;

            P = Link[P];
        }

        if (P == -1) {
            Link[New] = 0;
        } else {
            valueType Q = Transfer[P][ch];

            if (Length[P] + 1 == Length[Q]) {
                Link[New] = Q;
            } else {
                valueType Clone = ++UsedPoolSize;

                Cloned[Clone] = true;

                Length[Clone] = Length[P] + 1;
                Link[Clone] = Link[Q];
                Transfer[Clone] = Transfer[Q];
                FirstPos[Clone] = FirstPos[Q];

                while (P != -1 && Transfer[P][ch] == Q) {
                    Transfer[P][ch] = Clone;

                    P = Link[P];
                }

                Link[Q] = Link[New] = Clone;
            }
        }
    }

public:
    // extra functions
    void GetTopoOrder() {
        TopoOrder.resize(UsedPoolSize + 1);

        ValueVector Count(N + 1, 0);

        for (valueType i = 0; i <= UsedPoolSize; ++i)
            ++Count[Length[i]];

        for (valueType i = 1; i <= N; ++i)
            Count[i] += Count[i - 1];

        for (valueType i = 0; i <= UsedPoolSize; ++i)
            TopoOrder[--Count[Length[i]]] = i;

        std::reverse(TopoOrder.begin(), TopoOrder.end());
    }

    void GetEndPosCount() {
        EndPosCount.resize(UsedPoolSize + 1);

        std::fill(EndPosCount.begin(), EndPosCount.end(), 0);

        for (valueType i = 0; i <= UsedPoolSize; ++i)
            if (!Cloned[i])
                EndPosCount[i] = 1;

        for (auto const &u : TopoOrder)
            if (Link[u] != -1)
                EndPosCount[Link[u]] += EndPosCount[u];

        EndPosCount[0] = 0;
    }

    valueType size() const {
        return UsedPoolSize + 1;
    }

    valueType GetLength(valueType u) const {
        return Length[u];
    }

    valueType GetLink(valueType u) const {
        return Link[u];
    }

    valueType GetFirstPos(valueType u) const {
        return FirstPos[u];
    }

public:
    // customed functions for selected problems (P8368)
    valueType Ans() const {
        valueType ans = N / 2;

        for (valueType i = 1; i <= UsedPoolSize; ++i)
            if (EndPosCount[i] > 1)
                ans = std::max(ans, Length[i] + (N - FirstPos[i]) / 2);

        return ans;
    }
};

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

    valueType T;

    std::cin >> T;

    for (valueType testcase = 0; testcase < T; ++testcase) {
        string S;

        std::cin >> S;

        SuffixAutomaton<26, 'a'> SAM(S);

        SAM.GetTopoOrder();
        SAM.GetEndPosCount();

        std::cout << SAM.Ans() << '\n';
    }

    return 0;
}

CF103E Buying Sets

由于已经满足对于任意 \(k\) 个集合的大小不小于 \(k\)。因此只需要保证对于任意 \(k\) 个集合的大小不大于 \(k\) 即可满足题目限制要求。

这也就是意味着只要选择了一个元素,那么就必须选择一个集合,否则该解就是不合法的。

不妨将集合的权值均增加一个较大值,同时让每个元素的权值均减少一个较大值,这里的较大值要满足任意答案减去这个值均不会是最优解。

剩下的问题就是如何求出最优解,由于元素有负权值,因此是有最小权闭合子图解决即可。

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

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

class Dinic {
private:
    struct Edge {
    public:
        typedef std::list<Edge> container;
        typedef container::iterator iterator;

        valueType to;
        valueType cap;
        valueType flow;
        iterator pair;

        Edge() : to(-1), cap(-1), flow(-1), pair(){};

        Edge(valueType to, valueType cap, iterator pair = iterator()) : to(to), cap(cap), flow(0), pair(pair){};
    };

    typedef std::vector<Edge::container> Graph;
    typedef std::vector<Edge::iterator> IterVector;

    valueType N;
    Graph G;
    ValueVector depth;
    IterVector start;
    bool Initialized;

public:
    explicit Dinic(valueType N) : N(N), G(N + 1), depth(N + 1, 0), start(N + 1), Initialized(false){};

    void addEdge(valueType from, valueType to, valueType cap) {
        if (__builtin_expect(Initialized, false))
            throw std::runtime_error("Dinic: addEdge after init");

        G[from].emplace_back(to, cap);
        G[to].emplace_back(from, 0);
        G[from].back().pair = std::prev(G[to].end());
        G[to].back().pair = std::prev(G[from].end());
    }

    void init() {
        if (__builtin_expect(Initialized, false))
            throw std::runtime_error("Dinic: init twice");

        Initialized = true;

        std::fill(depth.begin(), depth.end(), 0);

        for (valueType i = 1; i <= N; ++i)
            start[i] = G[i].begin();
    }

    void reset() {
        if (__builtin_expect(!Initialized, false))
            throw std::runtime_error("Dinic: reset before init");

        for (valueType i = 1; i <= N; ++i)
            for (auto &iter : G[i])
                iter.flow = 0;

        std::fill(depth.begin(), depth.end(), 0);

        Initialized = false;
    }

    valueType maxFlow(valueType S, valueType T) {
        if (__builtin_expect(!Initialized, false))
            throw std::runtime_error("Dinic: maxFlow before init");

        valueType result = 0;

        while (bfs(S, T)) {
            IterVector begin = start;

            result += dfs(S, T, std::numeric_limits<valueType>::max(), begin);
        }

        return result;
    }

private:
    bool bfs(valueType S, valueType T) {
        std::fill(depth.begin(), depth.end(), 0);

        std::queue<valueType> Q;

        Q.push(S);
        depth[S] = 1;

        while (!Q.empty()) {
            valueType const u = Q.front();

            Q.pop();

            for (auto const &e : G[u]) {
                if ((e.cap > e.flow) && (!depth[e.to])) {
                    depth[e.to] = depth[u] + 1;
                    Q.push(e.to);
                }
            }
        }

        return depth[T] > 0;
    }

    valueType dfs(valueType u, valueType T, valueType flow, IterVector &Begin) {
        if (u == T || flow == 0)
            return flow;

        valueType result = 0;

        for (auto &e = Begin[u]; e != G[u].end(); ++e) {
            if (e->cap > e->flow && depth[e->to] == depth[u] + 1) {
                valueType const f = dfs(e->to, T, std::min(flow - result, e->cap - e->flow), Begin);

                e->flow += f;
                e->pair->flow -= f;

                result += f;

                if (result == flow)
                    return flow;
            }
        }

        return result;
    }
};

constexpr valueType INF = std::numeric_limits<valueType>::max(), MAX = 1ll << 30;

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

    valueType N;

    std::cin >> N;

    ValueVector set(N + 1, 0), item(N + 1, 0);

    valueType size = 0;

    valueType const S = ++size, T = ++size;

    for (valueType i = 1; i <= N; ++i) {
        set[i] = ++size;
        item[i] = ++size;
    }

    Dinic dinic(size);

    valueType sum = 0;

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

        std::cin >> k;

        for (valueType j = 0; j < k; ++j) {
            valueType x;

            std::cin >> x;

            dinic.addEdge(set[i], item[x], INF);
        }
    }

    for (valueType i = 1; i <= N; ++i)
        dinic.addEdge(item[i], T, MAX);

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

        std::cin >> weight;

        dinic.addEdge(S, set[i], MAX - weight);

        sum += MAX - weight;
    }

    dinic.init();

    std::cout << -(sum - dinic.maxFlow(S, T)) << std::endl;

    return 0;
}

[HNOI2008] 玩具装箱

\(f_i\) 表示前 \(i\) 个箱子的最优解,那么有转移:

\[f_i = \min\limits_{j < i}\left\{f_j + \left(s_i - s_j - L\right)^2\right\} \]

其中 \(s_i = i + \sum\limits_{j \le i} c_j\)\(L\) 较题目给出值大 \(1\)

上式可以整理为:

\[f_i - \left(s_i - L\right)^2 = \min\limits_{j < i}\left\{f_j + s_j^2 + 2 \cdot s_j \cdot \left(s_i - L\right)\right\} \]

使用斜率优化即可。

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;

realType GetSlope(ValuePair const &A, ValuePair const &B) {
    return (realType) (B.second - A.second) / (B.first - A.first);
}

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

    valueType N, L;

    std::cin >> N >> L;

    L = L + 1;

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

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

        S[i] = S[i - 1] + C[i] + 1;
    }

    ValueVector F(N + 1, 0);

    PairVector Q;

    Q.reserve(N + 1);

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

    valueType pointer = 0;

    for (valueType i = 1; i <= N; ++i) {
        valueType const K = 2 * (S[i] - L);

        while (pointer + 1 < Q.size() && GetSlope(Q[pointer], Q[pointer + 1]) < K)
            ++pointer;

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

        F[i] = y - x * K + (S[i] - L) * (S[i] - L);

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

        while (Q.size() > 1 && GetSlope(Q[Q.size() - 2], Q[Q.size() - 1]) > GetSlope(Q[Q.size() - 1], Node))
            Q.pop_back();

        Q.push_back(Node);
    }

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

    return 0;
}

[APIO2010] 特别行动队

\(f_i\) 表示前 \(i\) 个人的最优解,那么有转移:

\[f_i = \max\limits_{j < i}\left\{f_j + a\left(s_i - s_j\right)^2 + b\left(s_i - s_j\right) + c\right\} \]

其中 \(s_i = \sum\limits_{j \le i} x_j\)

上式可以整理为:

\[f_i - \left(a \cdot s_i^2 + b \cdot s_i + c\right) = \max\limits_{j < i}\left\{f_j + a \cdot s_j^2 - b \cdot s_j - 2a \times s_i s_j\right\} \]

使用斜率优化即可。

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);
}

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);
}

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);
}

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

    valueType N, A, B, C;

    std::cin >> N >> A >> B >> C;

    ValueVector W(N + 1, 0), S(N + 1, 0);

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

        S[i] = S[i - 1] + W[i];
    }

    ValueVector F(N + 1, 0);

    PairVector Q;

    Q.reserve(N + 1);

    Q.emplace_back(2 * A * S[0], F[0] + A * S[0] * S[0] - B * S[0]);

    valueType pointer = 0;

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

        while (pointer + 1 < Q.size() && CompareGreater(Q[pointer], Q[pointer + 1], K))
            ++pointer;

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

        F[i] = y - x * K + A * S[i] * S[i] + B * S[i] + C;

        ValuePair const Node(2 * A * S[i], F[i] + A * S[i] * S[i] - B * S[i]);

        while (Q.size() > 1 && CompareLess(Q[Q.size() - 2], Q[Q.size() - 1], Node))
            Q.pop_back();

        Q.push_back(Node);
    }

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

    return 0;
}

[ZJOI2007] 仓库建设

\(f_i\) 表示前 \(i\) 个点的最优解,那么有转移:

\[f_i = \min\limits_{j < i}\left\{f_j + x_i\left(sp_i - sp_j\right) - s_i + s_j + c_i\right\} \]

其中 \(sp_i = \sum\limits_{j \le i} p_j, s_i = \sum\limits_{j \le i} p_j \cdot x_j\)

上式可以整理为:

\[f_i - sp_i \cdot x_i - c_i + s_i = \min\limits_{j < i}\left\{f_j + s_j - sp_j \cdot x_i\right\} \]

使用斜率优化即可。

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);
}

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);
}

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);
}

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

    valueType N;

    std::cin >> N;

    ValueVector X(N + 1, 0), P(N + 1, 0), C(N + 1, 0), S(N + 1, 0);

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

        S[i] = S[i - 1] + X[i] * P[i];
    }

    std::partial_sum(P.begin(), P.end(), P.begin());

    ValueVector F(N + 1, 0);

    PairVector Q;

    Q.reserve(N + 1);

    Q.emplace_back(P[0], F[0] + S[0]);

    valueType pointer = 0;

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

        while (pointer + 1 < Q.size() && CompareLess(Q[pointer], Q[pointer + 1], K))
            ++pointer;

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

        F[i] = y - x * K + P[i] * X[i] + C[i] - S[i];

        ValuePair const Node(P[i], F[i] + S[i]);

        while (Q.size() > 1 && CompareLess(Q[Q.size() - 2], Q[Q.size() - 1], Node))
            Q.pop_back();

        Q.push_back(Node);
    }

    valueType ans = std::numeric_limits<valueType>::max();

    for (valueType i = 1; i <= N; ++i)
        if (P[i] == P[N])
            ans = std::min(ans, F[i]);

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

    return 0;
}

[USACO08MAR] Land Acquisition G

由于所有的土地都需要购买,所以若某块土地的长和宽均小于某块其他土地,那么这块土地一定会被包含在其他土地中。

因此我们可以将所有的土地按长 \(h_i\) 的值递减排序,若土地 \(i\) 满足 \(\forall j < i, w_j < w_i\),那么这块土地不会被并购,其中 \(w_i\) 表示土地 \(i\) 的宽。

这样我们可以处理出一个土地序列,其长单调不增,宽单调不减。

这样若我们选择了一个区间 \(\left[l, r\right]\) 的土地,那么其花费即为 \(h_l \times w_r\),进而可以进行动态规划。

\(f_i\) 表示前 \(i\) 个土地的最优解,那么有转移:

\[f_i = \min\limits_{j < i}\left\{f_j + h_{j + 1} \times w_i\right\} \]

使用斜率优化即可。

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;

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);
}

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);
}

bool CompareLess(ValuePair const &A, ValuePair const &B, valueType const K) {
    if ((B.second - A.second) * (B.first - A.first) < 0 && K > 0)
        return true;
    else if ((B.second - A.second) * (B.first - A.first) > 0 && K < 0)
        return false;

    if ((B.second - A.second) < 0 && (B.first - A.first) < 0)
        return (B.second - A.second) > K * (B.first - A.first);
    else
        return (B.second - A.second) < K * (B.first - A.first);
}

bool CompareGreater(ValuePair const &A, ValuePair const &B, valueType const K) {
    if ((B.second - A.second) * (B.first - A.first) < 0 && K > 0)
        return false;
    else if ((B.second - A.second) * (B.first - A.first) > 0 && K < 0)
        return true;

    if ((B.second - A.second) < 0 && (B.first - A.first) < 0)
        return (B.second - A.second) < K * (B.first - A.first);
    else
        return (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;

    std::cin >> N;

    PairVector A(N);

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

    std::sort(A.begin(), A.end(), std::greater<>());

    PairVector B({ValuePair(1ll << 25, 0)});

    B.reserve(N);

    for (auto const &[x, y] : A) {
        if (B.empty() || B.back().second < y)
            B.emplace_back(x, y);
    }

    N = B.size() - 1;

    ValueVector H(N + 10, 1ll << 25), W(N + 10, 1ll << 25);

    for (valueType i = 0; i <= N; ++i)
        H[i] = B[i].first, W[i] = B[i].second;

    ValueVector F(N + 1, 0);

    PairVector Q;

    Q.reserve(N + 1);

    Q.emplace_back(-H[1], F[0]);

    valueType pointer = 0;

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

        while (pointer + 1 < Q.size() && CompareLess(Q[pointer], Q[pointer + 1], K))
            ++pointer;

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

        F[i] = y - x * K;

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

        while (Q.size() > 1 && CompareLess(Q[Q.size() - 2], Q[Q.size() - 1], Node)) {
            Q.pop_back();

            pointer = std::max<valueType>(pointer - 1, 0);
        }

        Q.push_back(Node);
    }

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

    return 0;
}
posted @ 2024-01-01 22:02  User-Unauthorized  阅读(36)  评论(2编辑  收藏  举报