Solution Set【2024.1.11】

A. 战争模拟器

fl,r,p 表示区间满足 argmaxlirAi=p 的情况下区间 [l,r] 的最大利益,有转移:

fl,r,p=maxlip1fl,p1,i+maxp+1irfp+1,r,i+maxk{Vp,k×lippirQi,jCp,k}

注意到若对于 p,q 满足 lp,qrAq<Ap,那么一定有 fl,r,q<fl,r,p。即可以最优方案中的 p 一定是最大值,因此我们可以考虑设 fl,r 表示区间 [l,r] 的最大利益,转移时枚举最大值,有:

fl,r=maxlpr{fl,p1+fp+1,r+maxk{Vp,k×lippirQi,jCp,k}}

发现转移式的后半部分可以写为斜率优化的形式,进而可以对于每个 p 建出凸包。

若直接在凸包上二分那么复杂度为 O(n3logk),无法接受。

考虑通过改变转移顺序使得在凸包上查询的斜率递增,发现可以先逆序枚举 l,后正序枚举 p,最后正序枚举 r。时间复杂度为 O(n3+nk+klogk),可以通过。

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 __int128_t MultiType;

constexpr valueType MIN = std::numeric_limits<valueType>::min(), MAX = std::numeric_limits<valueType>::max();

bool Compare(ValuePair const &A, ValuePair const &B, ValuePair const &C, ValuePair const &D) {
    assert(B.first - A.first > 0);
    assert(D.first - C.first > 0);

    return (__int128_t) (B.second - A.second) * (__int128_t) (D.first - C.first) >= (__int128_t) (D.second - C.second) * (__int128_t) (B.first - A.first);
}

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

bool Compare(ValuePair const &A, ValuePair const &B, valueType K) {
    assert(B.first - A.first > 0);

    return (__int128_t) (B.second - A.second) <= (__int128_t) (K) * (__int128_t) (B.first - A.first);
}

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

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

    valueType N;

    std::cin >> N;

    PairMatrix A(N + 1);
    ValueMatrix Q(N + 1, ValueVector(N + 1, 0));

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

    for (valueType i = 1; i <= N; ++i) {
        for (valueType j = 1; j <= N; ++j)
            Q[i][j] += Q[i - 1][j];
    }

    for (valueType i = 1; i <= N; ++i) {
        for (valueType j = 1; j <= N; ++j)
            Q[i][j] += Q[i][j - 1];
    }

    auto GetSum = [&](valueType l1, valueType l2, valueType r1, valueType r2) -> valueType {// get sum of Q[l][r] for l in [l1, l2] and r in [r1, r2]
        return Q[l2][r2] - Q[l1 - 1][r2] - Q[l2][r1 - 1] + Q[l1 - 1][r1 - 1];
    };

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

        std::cin >> K;

        PairVector B(K);

        for (auto &[v, c] : B)
            std::cin >> v >> c;

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

        A[i].reserve(K);

        for (auto const &iter : B) {
            auto const &[v, c] = iter;

            while (A[i].size() > 1 && (A[i].back().first == v || Compare(A[i][A[i].size() - 2], A[i].back(), iter)))
                A[i].pop_back();

            A[i].push_back(iter);
        }
    }

    ValueMatrix F(N + 2, ValueVector(N + 2, 0));

    for (valueType l = 1; l <= N; ++l)
        for (valueType r = l; r <= N; ++r)
            F[l][r] = MIN;

    for (valueType l = N; l >= 1; --l) {
        for (valueType p = l; p <= N; ++p) {
            valueType pointer = 0;

            for (valueType r = p; r <= N; ++r) {
                valueType const K = GetSum(l, p, p, r);

                while (pointer + 1 < A[p].size() && Compare(A[p][pointer], A[p][pointer + 1], K))
                    ++pointer;

                auto const &[x, y] = A[p][pointer];

                F[l][r] = std::max(F[l][r], F[l][p - 1] + F[p + 1][r] - (y - x * K));
            }
        }
    }

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

    return 0;
}

B. 种树

首先考虑如何计算根链期望长度 depthu,不难发现通过枚举其父亲节点可以得到转移式:

depthu=cu+p<uapsu1×(depthp+cp)

其中 si=jiaj,即 a 的前缀和。不难发现通过维护 p<uap×(depthp+cp) 的值可以在 O(1) 的时间内计算出 depthu。进而我们可以在 O(n) 的时间内计算出所有节点的 depth

考虑如何计算点对 u,v 间的期望距离,不难发现可以通过枚举其最近公共祖先 x 得到转移式(假设 u<v):

dist(u,v)=xuP(x)×(depthu+depthv2depthx)=depthu+depthv2x<uP(x)×depthx2P(u)×depthu

首先考虑如何计算 P(u),发现其等同于 uv 的根链上的概率,不妨假设路径 uv 上的节点依次为 k1,k2,,kl,那么这条路径出现的概率为:

ausk11×ak1sk21××aklsv1=ausv1×1ilakiski1

可以发现 P(u) 即为 u,v 之间所有可能的路径的概率之和,因此有:

P(u)=u<k1<k2<<kn<vausv1×1ilakiski1=ausv1×u<k1<k2<<kn<v1ilakiski1=ausv1u<i<v(1+aisi1)=ausv1u<i<vai+si1si1=ausv1u<i<vsisi1=ausu

继续考虑对于 x<u,如何计算 P(x),不妨将点分为两部分,第一部分为标号在 (x,u) 之间的点,第二部分为标号在 (u,v) 之间的点。

对于第一部分的点,其可能的出现情况为:

  • 不在路径中出现
  • 仅在路径 xv 中出现
  • 仅在路径 xu 中出现

对于第二部分的点,其可能的出现情况为:

  • 不在路径中出现
  • 仅在路径 xv 中出现

因此有:

P(x)=axsu1×axsv1×x<i<u(1+aisi1+aisi1)×u<i<v(1+aisi1)=ax2su1×sv1×x<i<u2ai+si1si1×u<i<vai+si1si1=ax2su1×sv1×x<i<u2ai+si1si1×u<i<vsisi1=ax2su1×su×x<i<u2ai+si1si1

可以发现该式仅与 u 有关,因此可以快速计算。

总复杂度为 O(n×f(p)+q),其中 f(p) 表示计算某数在模 p 意义下的逆元的复杂度。

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;

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

    template<typename T1, typename T2>
    void Inc(T1 &a, T2 b) {
        a = a + b;

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

    template<typename T1, typename T2>
    void Dec(T1 &a, T2 b) {
        a = a - b;

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

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

    template<typename T1, typename T2>
    T1 sub(T1 a, T2 b) {
        return a - b < 0 ? a - b + MOD : a - b;
    }

    template<typename T1, typename T2>
    T1 mul(T1 a, T2 b) {
        return (long long) a * b % MOD;
    }

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

    template<typename T1, typename T2>
    T1 pow(T1 a, T2 b) {
        T1 result = 1;

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

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

        return result;
    }
}// namespace MODINT_WITH_FIXED_MOD

using namespace MODINT_WITH_FIXED_MOD;

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

    valueType N, Q;

    std::cin >> N >> Q;

    ValueVector A(N + 1, 0);
    ValueVector C(N + 1, 0);
    ValueVector S(N + 1, 0);   // sum of A
    ValueVector InvS(N + 1, 0);// inverse of S
    ValueVector D(N + 1, 0);   // depth to root
    ValueVector L(N + 1, 0);   // lca depth

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

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

    std::partial_sum(A.begin(), A.end(), S.begin());

    InvS = S;

    for (valueType i = 0; i <= N; ++i)
        InvS[i] = pow(S[i], MOD - 2);

    valueType WeightedSum = mul(A[1], sum(C[1], D[1]));

    for (valueType i = 2; i <= N; ++i) {
        D[i] = sum(mul(WeightedSum, InvS[i - 1]), C[i]);

        Inc(WeightedSum, mul(A[i], sum(C[i], D[i])));
    }

    valueType Sum = 0;

    for (valueType i = 2; i <= N; ++i) {
        L[i] = mul(Sum, mul(InvS[i - 1], InvS[i]));

        Mul(Sum, mul(sum(2 * A[i], S[i - 1]), InvS[i - 1]));

        Inc(Sum, mul(D[i], A[i] * A[i]));
    }

    for (valueType q = 0; q < Q; ++q) {
        valueType u, v;

        std::cin >> u >> v;

        if (u == v) {
            std::cout << 0 << '\n';

            continue;
        }

        if (u > v)
            std::swap(u, v);

        valueType ans = sum(D[u], D[v]);

        Dec(ans, mul(2, L[u]));

        Dec(ans, mul(mul(2, D[u]), mul(A[u], InvS[u])));

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

    std::cout << std::flush;

    return 0;
}
posted @   User-Unauthorized  阅读(13)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
· 【杂谈】分布式事务——高大上的无用知识?
点击右上角即可分享
微信分享提示