题意
传送门 P4043 [AHOI2014/JSOI2014]支线剧情
题解
游戏剧情构成一个 DAG,一次游戏花费的时间等于所走剧情路线花费的时间和。那么问题转化为边容量有上下界限制的最小费用流问题。设置超级汇点,与所有剧情点连边后,跑有源汇上下界最小费用流即可(基本思路是假设下界限制得到满足,然后尽量使流量平衡)。
#include <bits/stdc++.h>
using namespace std;
template <typename Cap, typename Cost>
struct MCF
{
struct edge
{
int to;
Cap cap;
int rev;
Cost cost;
};
int V;
vector<vector<edge>> G;
vector<Cost> h, ds;
vector<int> prevv, preve;
vector<bool> used;
MCF(int n) : V(n), G(n), h(n), ds(n), prevv(n), preve(n), used(n) {}
void add_edge(int from, int to, Cap cap, Cost cost)
{
G[from].push_back({to, cap, (int)G[to].size(), cost});
G[to].push_back({from, 0, (int)G[from].size() - 1, -cost});
}
pair<Cap, Cost> min_cost_flow(int s, int t, Cap f = numeric_limits<Cap>::max())
{
Cost res = 0;
while (f > 0)
{
priority_queue<pair<Cost, int>, vector<pair<Cost, int>>, greater<pair<Cost, int>>> q;
fill(ds.begin(), ds.end(), numeric_limits<Cost>::max());
fill(used.begin(), used.end(), 0);
ds[s] = 0;
q.push({0, s});
while (!q.empty())
{
int v = q.top().second;
q.pop();
if (used[v])
continue;
used[v] = 1;
for (int i = 0; i < (int)G[v].size(); ++i)
{
auto &e = G[v][i];
Cost d = ds[v] + e.cost + h[v] - h[e.to];
if (e.cap > 0 && ds[e.to] > d)
{
ds[e.to] = d;
prevv[e.to] = v, preve[e.to] = i;
q.push({d, e.to});
}
}
}
if (ds[t] == numeric_limits<Cost>::max())
return {numeric_limits<Cap>::max() - f, res};
for (int v = 0; v < V; ++v)
h[v] += ds[v];
Cap d = f;
for (int v = t; v != s; v = prevv[v])
d = min(d, G[prevv[v]][preve[v]].cap);
f -= d;
res += d * h[t];
for (int v = t; v != s; v = prevv[v])
{
auto &e = G[prevv[v]][preve[v]];
e.cap -= d;
G[v][e.rev].cap += d;
}
}
return {f, res};
}
};
template <typename Cap, typename Cost>
struct edge
{
int to;
Cap lb, ub;
Cost cost;
};
vector<vector<edge<int, int>>> G;
template <typename Cap, typename Cost>
pair<Cap, Cost> solve(int s, int t)
{
int N = G.size();
int S = N, T = S + 1;
int V = T + 1;
MCF<Cap, Cost> flow(V);
vector<Cap> f(N);
Cost add = 0;
for (int v = 0; v < N; ++v)
for (auto &e : G[v])
{
Cap x = e.lb;
f[e.to] += x, f[v] -= x;
add += x * e.cost;
}
Cap sum = 0;
for (int v = 0; v < N; ++v)
{
if (f[v] > 0)
{
sum += f[v];
flow.add_edge(S, v, f[v], 0);
}
else if (f[v] < 0)
flow.add_edge(v, T, -f[v], 0);
}
for (int v = 0; v < N; ++v)
for (auto &e : G[v])
flow.add_edge(v, e.to, e.ub - e.lb, e.cost);
flow.add_edge(t, s, numeric_limits<Cap>::max(), 0);
auto res = flow.min_cost_flow(S, T);
assert(sum == res.first);
res.second += add;
return res;
}
int main()
{
ios::sync_with_stdio(0), cin.tie(0);
int N;
cin >> N;
G.resize(N + 1);
int inf = 1e9;
for (int i = 0; i < N; ++i)
{
int k, b, t;
cin >> k;
for (int j = 0; j < k; ++j)
{
cin >> b >> t;
G[i].push_back({b - 1, 1, inf, t});
}
G[i].push_back({N, 0, inf, 0});
}
auto res = solve<int, int>(0, N);
cout << res.second << '\n';
return 0;
}