首页 > 其他分享 >[ARC107F] Sum of Abs 题解

[ARC107F] Sum of Abs 题解

时间:2023-11-15 14:57:45浏览次数:39  
标签:std right 题解 Sum ARC107F flow depth valueType left

题意

给定一个 \(N\) 个点,\(M\) 条边的简单无向图,每个节点有两个值 \(A_i\) 和 \(B_i\)。

现对于每个节点,均可以选择花费 \(A_i\) 的代价将其删去或保留节点。若一个节点被删除,那么所有与其向连的边也会被删除。

定义一个极大联通块的权值为联通块内所有节点的 \(B_i\) 的和的绝对值,即 \(\left\lvert\sum B_i\right\rvert\)。定义整个图的权值为所有极大联通块的权值的和。

最大化操作后图的权值减去所有花费后的值。

  • \(1 \le N \le 300\)
  • \(1 \le M \le 300\)
  • \(1 \le A_i \le 10^6\)
  • \(-10^6 \le B_i \le 10^6\)

题解

可以发现每个节点有三种方式来产生贡献:

  • 其所在联通块权值和为负,产生贡献 \(-B_i\)
  • 其所在联通块权值和为正,产生贡献 \(B_i\)
  • 其被删除,不产生贡献

那么我们可以通过对每个节点赋一个权值 \(C_i \in \left\{-1, 0, 1\right\}\) 来描述其产生的贡献,那么问题变为了最大化

\[\sum\limits_{i = 1}^{N}C_i \times B_i \]

可以发现一个联通块的内节点的权值 \(C_i\) 在除去 \(0\) 后一定相同,因此题目中的边的限制可以转化为:

  • 对于任意一条边 \(\left(u, v\right)\),不存在 \(C_u = 1 \land C_v = -1\) 或 \(C_u = -1 \land C_v = 1\)

考虑使用最小割来处理限制。

首先可以发现在不考虑限制的情况下最优价值为 \(\sum\limits_{i = 1}^{N}\left\lvert B_i \right\rvert\),即 \(C_i\) 与 \(B_i\) 的正负性相同。

考虑将每个节点 \(i\) 拆分为网络中的两个节点 \(X_i\) 和 \(Y_i\)。建边 \(Y_i \rightarrow X_i\),流量为 \(\infty\)。可以发现在此之后 \(X_i\) 和 \(Y_i\) 与源点 \(S\) 联通或是与汇点 \(T\) 联通的状态只有三种:\(\left(S, S\right)\), \(\left(S, T\right)\), \(\left(T, T\right)\),考虑将 \(C_i\) 的三种可能值与之对应,有

  • \(C_i = 1 \Leftrightarrow \left(S, S\right)\)
  • \(C_i = 0 \Leftrightarrow \left(S, T\right)\)
  • \(C_i = -1 \Leftrightarrow \left(T, T\right)\)

接下来考虑如何建图。

首先对于每个节点:

  • 若将其删除,那么需要花费的代价为 \(A_i + \left\lvert B_i \right\rvert\)。故可建边 \(X_i \rightarrow Y_i\),流量为 \(A_i + \left\lvert B_i \right\rvert\)。

  • 若 \(B_i > 0\),那么 \(C_i = 1\) 对应的情况无需花费,若 \(C_i = -1\) 那么需要的花费为 \(2 \times \left\lvert B_i \right\rvert\),可以建边 \(S \rightarrow X_i\),流量为 \(2 \times \left\lvert B_i \right\rvert\)。

  • 若 \(B_i < 0\),那么 \(C_i = -1\) 对应的情况无需花费,若 \(C_i = 1\) 那么需要的花费为 \(2 \times \left\lvert B_i \right\rvert\),可以建边 \(Y_i \rightarrow T\),流量为 \(2 \times \left\lvert B_i \right\rvert\)。

接下来考虑如何处理边的限制。

对于边 \(\left(u, v\right)\),建边 \(Y_u \rightarrow X_v\) 和 \(Y_v \rightarrow X_u\),流量均为 \(\infty\)。

可以发现边的限制不合法当且仅当节点 \(u\) 和节点 \(v\) 的与源汇联通状态分别为 \(\left(S, S\right)\) 和 \(\left(T,T\right)\) 或 \(\left(T, T\right)\) 和 \(\left(S,S\right)\),按上文建边可以避免这两种情况的出现。

\(\sum\limits_{i = 1}^{N}\left\lvert B_i \right\rvert\) 减去网络的最小割即为答案。

最终网络中的点数为 \(\mathcal{O}(N)\),边数为 \(\mathcal{O}(N + M)\)。复杂度为 \(\mathcal{O}\left(N^2 \left(N + M\right)\right)\),可以通过。

Code

#include <bits/stdc++.h>

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

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

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

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

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

    valueType N, M;

    std::cin >> N >> M;

    valueType const size = 2 * N + 2, S = 2 * N + 1, T = 2 * N + 2;

    Dinic dinic(size);

    ValueVector A(N + 1), B(N + 1), X(N + 1), Y(N + 1);

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

        Y[i] = N + i;
    }

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

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

    for (valueType i = 1 ;i <= N; ++i) {
        dinic.addEdge(Y[i], X[i], INF);

        dinic.addEdge(X[i], Y[i], A[i] + std::abs(B[i]));

        if (B[i] > 0) {
            dinic.addEdge(S, X[i], 2 * std::abs(B[i]));
        } else {
            dinic.addEdge(Y[i], T, 2 * std::abs(B[i]));
        }
    }

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

        std::cin >> u >> v;

        dinic.addEdge(Y[u], X[v], INF);
        dinic.addEdge(Y[v], X[u], INF);
    }

    valueType sum = 0;

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

    dinic.init();

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

    return 0;
}

标签:std,right,题解,Sum,ARC107F,flow,depth,valueType,left
From: https://www.cnblogs.com/User-Unauthorized/p/solution-ARC107F.html

相关文章

  • 电脑网站支付报错“验签出错,建议检查签名字符串或私钥与应用公钥是否匹配”问题解决记
    在对接支付宝电脑网站支付的时候,遇到如下报错:“错误代码invalid-signature错误原因:验签出错,建议检查签名字符串或签名私钥与应用公钥是否匹配”。但展示的报错内容跟实际原因有所出入(在下文中有解答),这里记录下问题的解决排查过程。 问题复现在对接电脑网站支付时,生成......
  • 【洛谷 P1089】[NOIP2004 提高组] 津津的储蓄计划 题解(循环)
    [NOIP2004提高组]津津的储蓄计划题目描述津津的零花钱一直都是自己管理。每个月的月初妈妈给津津元钱,津津会预算这个月的花销,并且总能做到实际花销和预算的相同。为了让津津学习如何储蓄,妈妈提出,津津可以随时把整百的钱存在她那里,到了年末她会加上还给津津。因此津津制定了一......
  • 【题解 P2048】 超级钢琴
    [NOI2010]超级钢琴题目描述小Z是一个小有名气的钢琴家,最近C博士送给了小Z一架超级钢琴,小Z希望能够用这架钢琴创作出世界上最美妙的音乐。这架超级钢琴可以弹奏出\(n\)个音符,编号为\(1\)至\(n\)。第\(i\)个音符的美妙度为\(A_i\),其中\(A_i\)可正可负。一个......
  • Linux openssh问题解决: Permission denied, please try again
    1.vim打开sshd_config文件vim/etc/ssh/sshd_config2.搜索PermitRootLogin ,将 PermitRootLoginprohibie-password 改为如下:PermitRootLoginyes  ......
  • 洛谷 P1931 题解
    三倍经验P1931UVA436SP9340题意给你\(n(n\le30)\)种货币及\(m\)种汇率,问是否出现套利的情况。怎么没给\(m\)的范围啊思路首先把汇率抽象成一张图。容易发现,若一个单位的某种货币经过一个环获得了大于一的代价,说明出现了套利。具体来说,考虑Floyd,若$\existsi\in......
  • CF1542E2 Abnormal Permutation Pairs (hard version) 题解
    怎么会有这么离谱的题目啊。【模板】前缀和优化dp。思路考虑一个基本的东西。由于要求字典序的限制。我们可以枚举最长公共前缀计算。考虑如何求长度为\(i\)的排列有\(j\)个逆序对的数量。设\(dp_{i,j}\)。\[dp_{i,j}=\sum_{k=0}^{i-1}dp_{i-1,j-k}\]就是枚举新的......
  • [题解] CF1051F The Shortest Statement
    TheShortestStatement给一张\(n\)个点\(m\)条边的无向连通图,保证\(m-n\le20\),\(q\)次询问求两个点间的最短路。\(n,m,q\le10^5\)。由于边数只比点数多20,所以如果我们建出这张图的一棵生成树,那么非树边至多有21条。那么现在两点之间的最短路就转化成了不......
  • 「NOIP2014」解方程 题解
    思路首先我们可以观察到\(n\)和\(m\)与\(a_i\)相比小的很多,所以我们可以考虑直接暴力求解但是\(a_i\)太大了,所以如果需要直接计算的话需要全程使用高精度算法。因为高精度算法代码量有大速度又慢我们可依考虑将\(a_i\)转化为一个极大的指数取模的结果,因为只有是模数的......
  • Q7.4.1.2. 奇怪的方格涂色 题解
    原题链接首先想到暴力网络流:考虑最小割,\(S\)表示染黑色,\(T\)表示染白色。每个格子\(i\),连\((S,i,b_i)\),\((i,T,w_i)\)。怎么处理“奇怪的方格”?连\((i,i^\prime,p_i)\)和\((i^\prime,j,+\infty)\)。表示如果\(i\)归属于\(S\)(染黑色),那么就只能忍受奇怪所带来的\(p_i\)......
  • AT_abc230_f [ABC230F] Predilection 题解
    prelogue各位在比赛的时候一定要坚信自己的式子,然后去考虑自己的实现是不是挂了。本人在今天模拟赛的时候质疑自己的式子然后不看实现100->0。analysis考虑对这个给定数组进行前缀和,然后就将问题转化成为了求这个前缀和数组的子序列的个数。对于求子序列,我们很轻松可以写出......