P4043 [AHOI2014/JSOI2014]支线剧情(有源汇上下界最小费用可行流)

传送门

约束每个点至少要经过一次,因此是上下界网络流。

每经过边都需要相应的边权,且要求耗费边权之和最小,因此是最小费用流。

存在多个终点,需要建立汇点 t t t ,因此是有源汇网络流。

即:有源汇上下界最小费用可行流。

建图

本题中结点 1 1 1 即可作为源点。

对于原图中存在的所有边,建下界为 1 1 1,上界为 i n f inf inf ,费用为对应边权的边。

由于存在多个终点,需要建立汇点 t 1 t1 t1,将除了 1 1 1 号点外的所有边向 t 1 t1 t1 连容量为 i n f inf inf 费用为 0 0 0 的边。

新建平衡源点 s 2 s2 s2 和平衡汇点 t 2 t2 t2,使原图中的点流量平衡。(具体原理和操作可见:网络流中有关上下界限制的学习笔记

跑平衡源点 s 2 s2 s2 到平衡汇点 t 2 t2 t2 的最小费用最大流,即为可行流。注意由于每个边的下界为 1 1 1 ,因此最终答案还需要加上所有边的费用 ∗ 1 *1 1


参考代码

#include <bits/stdc++.h>
#define itn int
#define int long long
#define endl "\n"
#define PII pair<int, int>
using namespace std;
const int N = 2e5 + 10;
const int M = 5e4 + 10;
const itn inf = 0x3f3f3f3f;
const int mod = 1e9 + 7;
// const int mod = 998244353;

template <int N>
struct MCMF {
    struct E {
        int to, cap, val, inv;
    };
    vector<E> g[N];
    int dis[N], now[N], h[N], pre[N], preu[N];
    void add(int u, int v, int f, int w) {
        g[u].push_back({v, f, w, (int)g[v].size()});
        g[v].push_back({u, 0, -w, (int)g[u].size() - 1});
    }
    void dijkstra(int st) {
        priority_queue<pair<int, int>, vector<pair<int, int>>,
                       greater<pair<int, int>>>
            q;
        memset(dis, 0x3f, sizeof dis);
        memset(pre, -1, sizeof pre);
        memset(preu, -1, sizeof preu);
        dis[st] = 0;
        q.push({0, st});
        while (q.size()) {
            // auto [d, u] = q.top();
            auto qnow = q.top();
            int d = qnow.first, u = qnow.second;
            q.pop();
            if (dis[u] < d)
                continue;
            int x = 0;
            for (auto gnow : g[u]) {
                int v = gnow.to, f = gnow.cap, w = gnow.val, inv = gnow.inv;
                if (f && dis[v] > dis[u] + w + h[u] - h[v]) {
                    dis[v] = dis[u] + h[u] - h[v] + w;
                    pre[v] = x;
                    preu[v] = u;
                    q.push({dis[v], v});
                }
                x++;
            }
        }
    }
    pair<int, int> min_cost_max_flow(int st, int ed) {
        memset(h, 0, sizeof h);
        for (int flow = 0, cost = 0, res = inf;; res = inf) {
            dijkstra(st);
            if (dis[ed] > inf)
                return {flow, cost};
            for (int i = 0; i < N; i++) {
                h[i] += dis[i];
            }
            for (int i = ed; i != st; i = preu[i]) {
                res = min(res, g[preu[i]][pre[i]].cap);
            }
            flow += res;
            cost += res * h[ed];
            for (int i = ed; i != st; i = preu[i]) {
                g[i][g[preu[i]][pre[i]].inv].cap += res;
                g[preu[i]][pre[i]].cap -= res;
            }
        }
    }
};
MCMF<505> mcmf;
int n, m, s1, t1, s2, t2;
int in[N];

void ins(int u, int v, int l, int r, int w) {
    mcmf.add(u, v, r - l, w);
    in[u] -= l, in[v] += l;
}

void solve() {
    cin >> n;
    s1 = n + 1, t1 = n + 2;
    s2 = n + 3, t2 = n + 4;
    int sum = 0;
    for (int i = 1; i <= n; i++) {
        int k;
        cin >> k;
        while (k--) {
            int v, w;
            cin >> v >> w;
            ins(i, v, 1, inf, w);
            sum += w;
        }
    }
    for (int i = 2; i <= n; i++) {
        mcmf.add(i, t1, inf, 0);
    }

    for (int i = 1; i <= t1; i++) {
        if (in[i] < 0)
            mcmf.add(i, t2, -in[i], 0);
        else if (in[i] > 0)
            mcmf.add(s2, i, in[i], 0);
    }
    mcmf.add(t1, 1, inf, 0);
    cout << mcmf.min_cost_max_flow(s2, t2).second + sum;
}

signed main() {
    ios_base::sync_with_stdio(false), cin.tie(0), cout.tie(0);
    cout << fixed << setprecision(12);
    // init();
    int T = 1;
    // cin >> T;
    while (T--) {
        solve();
    }
    return 0;
}
AHOI2001是一种用于处理模式匹配和字符串搜索的经典算法,全称为"Another Happy Odyssey in 2001"。它通常应用于构建高效、空间优化的KMP(Knuth-Morris-Pratt)算法的一种改进版本。这种有限自动机常用于处理字符串搜索问题,尤其是在处理大量文本数据时。 关于题目代码的具体内容,这通常涉及到编程竞赛或算法实现题。通常,你需要编写一段程序,包括定义一个有限状态机(Finite Automaton),处理输入字符串和模式串,并根据AHOI2001算法来查找模式是否在原字符串中。关键部分会涉及如何创建前缀函数表、动态规划和自适应策略。 由于这不是一个直接的答案,下面是一个简化版的代码框架示例(假设用Python): ```python class AhoCorasickAutomaton: def __init__(self, patterns): self.prefix_func = self.build_prefix_function(patterns) def build_prefix_function(self, patterns): # 建立前缀函数表的计算过程... pass def search(self, text): index = 0 for pattern in patterns: while index < len(text) and index + len(pattern) <= len(text): if self.match(text[index:], pattern): return True index += self.prefix_func[pattern] return False def match(self, text, pattern): # 匹配函数,比较两个字符串是否相等... pass # 使用示例: patterns = ['AB', 'AC'] # 输入模式列表 automaton = AhoCorasickAutomaton(patterns) text = 'ABCABCD' # 待搜索的字符串 if automaton.search(text): print("Pattern found") else: print("Pattern not found")
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值