Solution Set【2024.1.18】

A. 树上横跳

发现可以将问题转化为将路径 \(s \rightarrow t\) 划分为若干段,每段的贡献为段首元素的编号乘上段长,然后求和。

发现其可以通过自底而上维护一个单调递减的栈来解决,考虑如何处理多组询问。

考虑倍增,这样路径就划分为了 \(\mathcal{O}(\log n)\) 段,我们考虑如何将这些段合并起来。由于我们是将区间倍增得来的,因此对于每个长度大于 \(1\) 的区间我们均可以将其划分为两个子区间,我们按其左侧的最小值进行分类讨论:

  • 若左侧的最小值大于该区间左端点,直接输出该区间内的单调栈的答案即可。
  • 若左侧的最小值小于等于该区间的最小值,那么这个区间内的全部元素均可以被左侧的最小值所覆盖,因此我们可以将其合并到左侧的区间中。
  • 否则考虑左侧的子区间,若其会被覆盖,那么递归的合并左区间,否则合并右区间。

复杂度为 \(\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::pair<valueType, valueType> ValuePair;
typedef std::vector<ValuePair> PairVector;
typedef std::vector<PairVector> PairMatrix;
typedef std::stack<valueType> ValueStack;

valueType N, M, K;
PairMatrix G;
ValueVector dist, depth, leftBound, rightBound;
ValueMatrix Father, F, Min;

valueType merge(valueType left, valueType x, valueType k) {
    if (Min[k][x] >= left)
        return left * (dist[x] - dist[Father[k][x]]);

    if (left > Father[k][x])
        return F[k][x];

    valueType const y = Father[k - 1][x];

    if (Min[k - 1][y] >= left)
        return left * (dist[y] - dist[Father[k][x]]) + merge(left, x, k - 1);
    else
        return F[k][x] - F[k - 1][y] + merge(left, y, k - 1);
}

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

    Father[0][x] = from;
    Min[0][x] = from;
    depth[x] = depth[from] + 1;
    leftBound[x] = ++dfsCount;

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

        dist[to] = dist[x] + weight;
        F[0][to] = x * weight;

        dfs(to, x);
    }

    rightBound[x] = dfsCount;
}

void build(valueType x, valueType from) {
    for (valueType i = 1; i <= K; ++i) {
        Father[i][x] = Father[i - 1][Father[i - 1][x]];

        if (Father[i][x] == 0)
            break;

        Min[i][x] = std::min(Min[i - 1][x], Min[i - 1][Father[i - 1][x]]);
        F[i][x] = F[i - 1][Father[i - 1][x]] + merge(Min[i - 1][Father[i - 1][x]], x, i - 1);
    }

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

        build(to, x);
    }
}

bool InTree(valueType u, valueType v) {// check if v is in the subtree of u
    return leftBound[u] <= leftBound[v] && rightBound[v] <= rightBound[u];
}

bool isChain() {
    for (valueType i = 1; i <= N; ++i) {
        if (G[i].size() > 2)
            return false;
    }

    return true;
}

namespace Chain {// 离线询问,时空复杂度 O(n log n + m)
    void solve() {
        PairMatrix Q(N + 1);
        ValueVector Ans(M);

        for (valueType m = 0; m < M; ++m) {
            valueType u, v;

            std::cin >> u >> v;

            if (!InTree(u, v))
                Ans[m] = -1;
            else if (u == v)
                Ans[m] = 0;
            else
                Q[u].emplace_back(v, m);
        }

        valueType Leaf = 1;

        for (valueType i = 2; i <= N; ++i) {
            if (depth[i] > depth[Leaf])
                Leaf = i;
        }

        PairVector stack;// first:节点编号,second:从栈底到该节点的带权路径和

        stack.reserve(N);

        stack.emplace_back(Leaf, 0);

        // for (auto const &[v, id] : Q[Leaf]) {
        //     if (v == Leaf)
        //         Ans[id] = 0;
        //     else
        //         std::abort();
        // }
        assert(Q[Leaf].empty());

        valueType x = Father[0][Leaf];

        while (x != 0) {
            while (stack.size() > 1 && stack.back().first > x) {
                stack.pop_back();
            }

            stack.emplace_back(x, stack.back().second + (dist[stack.back().first] - dist[x]) * x);

            for (auto const &[v, id] : Q[x]) {
                valueType l = 0, r = stack.size() - 1, pos = 0;

                while (l <= r) {
                    valueType const mid = (l + r) / 2;

                    if (InTree(stack[mid].first, v)) {
                        pos = mid;

                        r = mid - 1;
                    } else {
                        l = mid + 1;
                    }
                }

                Ans[id] = stack.back().second - stack[pos].second + (dist[v] - dist[stack[pos].first]) * stack[pos].first;
            }

            x = Father[0][x];
        }

        for (auto const &ans : Ans)
            std::cout << ans << '\n';

        std::cout << std::flush;
    }
}// namespace Chain

valueType solve(valueType u, valueType v) {
    if (!InTree(u, v))
        return -1;

    if (u == v)
        return 0;

    valueType const difference = depth[v] - depth[u];

    PairVector stack;

    stack.reserve(K);

    for (valueType i = 0; i <= K; ++i) {
        if ((difference >> i) & 1) {
            stack.emplace_back(v, i);

            v = Father[i][v];
        }
    }

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

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

    for (auto const &[x, k] : stack) {
        ans += merge(min, x, k);

        min = std::min(min, Min[k][x]);
    }

    return ans;
}

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

#ifndef LOCAL_STDIO
    freopen("tree.in", "r", stdin);
    freopen("tree.out", "w", stdout);
#endif

    std::cin >> N >> M;

    K = std::__lg(N);

    G.resize(N + 1);
    dist.resize(N + 1);
    depth.resize(N + 1);
    leftBound.resize(N + 1);
    rightBound.resize(N + 1);

    Father.resize(K + 1, ValueVector(N + 1));
    F.resize(K + 1, ValueVector(N + 1));
    Min.resize(K + 1, ValueVector(N + 1, std::numeric_limits<valueType>::max()));

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

    if (isChain()) {
        Chain::solve();

        return 0;
    }

    build(1, 0);

    for (valueType m = 0; m < M; ++m) {
        valueType u, v;

        std::cin >> u >> v;

        std::cout << solve(u, v) << '\n';
    }

    std::cout << std::flush;

    return 0;
}

B. 定价

贪心策略是显然的,考虑如何维护。

发现我们每次只需要维护当前数不可能为 \(1\) 的且在前一个数出现过的位置开始向高位考虑即可,不难发现每次插入的位置满足单调性,因此可以使用栈来维护为 \(1\) 的位置。

考虑如何寻找位置,当当前位被置为 \(1\) 时,找到这一位置上第一个不为 \(1\) 的位置即可,这可以通过 std::bitset 来实现。

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::vector<ValueSet> SetVector;
typedef std::tuple<valueType, valueType, valueType> ValueTuple;
typedef std::vector<ValueTuple> TupleVector;

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;

constexpr valueType V = 500000, MAXN = 1005;

typedef std::bitset<V> Bitset;
typedef std::vector<std::bitset<MAXN>> BitMatrix;

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

#ifndef LOCAL_STDIO
    freopen("price.in", "r", stdin);
    freopen("price.out", "w", stdout);
#endif

    valueType N, M, Q;

    std::cin >> N >> M >> Q;

    TupleVector oper(Q);
    ValueVector pool;

    for (auto &[type, r, c] : oper) {
        std::cin >> type;

        if (type == 2)
            continue;

        std::cin >> r >> c;

        pool.emplace_back(c);
    }

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

    pool.erase(std::unique(pool.begin(), pool.end()), pool.end());

    ValueVector power = pool;

    for (auto &c : power)
        c = pow(2, M - c);

    for (auto &[type, r, c] : oper) {
        if (type == 2)
            continue;

        c = std::distance(pool.begin(), std::lower_bound(pool.begin(), pool.end(), c));
    }

    BitMatrix Empty(pool.size());

    for (auto &bit : Empty)
        bit.set();

    SetVector S(N + 1);

    ValueMatrix G(N + 2);

    auto Query = [&]() -> valueType {
        ValueVector stack;
        stack.reserve(N);
        Bitset inStack;
        inStack.reset();

        valueType result = 0, sum = 0;

        for (valueType i = 1; i <= N; ++i) {
            valueType pos = pool.size();

            for (auto const &c : G[i])
                if (Empty[c][i] && inStack[c])
                    pos = std::min(pos, c);

            G[i].clear();

            if (S[i].lower_bound(pos) == S[i].begin())
                return -1;

            auto iter = std::make_reverse_iterator(S[i].lower_bound(pos));

            bool flag = false;

            while (iter != S[i].rend()) {
                auto const &c = *iter;

                if (!inStack[c]) {
                    while (!stack.empty() && stack.back() > c) {
                        inStack.reset(stack.back());
                        Dec(sum, power[stack.back()]);
                        stack.pop_back();
                    }

                    stack.emplace_back(c);
                    inStack.set(c);

                    Inc(sum, power[c]);
                    Inc(result, sum);

                    G[Empty[c]._Find_next(i)].emplace_back(c);
                    flag = true;
                    break;
                }

                ++iter;
            }

            if (!flag)
                return -1;
        }

        return result;
    };

    for (auto const &[type, r, c] : oper) {
        if (type == 1) {
            if (Empty[c][r]) {
                Empty[c].reset(r);

                S[r].insert(c);
            } else {
                Empty[c].set(r);

                S[r].erase(c);
            }
        } else {
            std::cout << Query() << '\n';
        }
    }

    return 0;
}
posted @ 2024-01-18 21:29  User-Unauthorized  阅读(28)  评论(0编辑  收藏  举报