T1 P4043 [AHOI2014/JSOI2014]支线剧情
思路:
每条边下限为 1,上限为 INF
将每个点连一条到 1 号节点,流量 INF,费用 0的边
跑上下界网络流即可
最小费用可行流:
考虑一张网络流图,每条边定义为
(
u
,
v
,
l
,
r
,
c
)
(u,v,l,r,c)
(u,v,l,r,c),代表从
u
u
u 到
v
v
v 的一条有向边,费用为
c
c
c,容量为
[
l
,
r
]
[l,r]
[l,r] 闭区间,已知源点
s
s
s 汇点
t
t
t,且保证源点没有入边、汇点没有出边
同时定义常规费用流图的边为
(
u
,
v
,
f
w
,
c
s
)
(u,v,fw,cs)
(u,v,fw,cs)
现在我们需要求这张图的最小费用可行流(就是满足所有边的流量上下限制,同时费用最小)
按照如下方式建立附加边和附加点:
- 建立附加源点 S S SS SS,和附加汇点 T T TT TT
- 对于原图中每一个点(包括源汇) u u u,令 d [ u ] d[u] d[u] 代表 u u u 点的所有入边的流量下界和减去出边的流量下界和
- 如果 d [ u ] d[u] d[u] 是负数,那么从 u u u 连一条边 ( u , T T , − d [ u ] , 0 ) (u,TT,-d[u],0) (u,TT,−d[u],0) 到 T T TT TT
- 如果 d [ u ] d[u] d[u] 是正数,那么从 S S SS SS 连一条边 ( S S , u , d [ u ] , 0 ) (SS,u,d[u],0) (SS,u,d[u],0) 到 u u u
- 对于原图中每一条边 ( u , v , l , r , c ) (u,v,l,r,c) (u,v,l,r,c),连边 ( u , v , r − l , c ) (u,v,r-l,c) (u,v,r−l,c)
- 连边 ( t , s , I N F , 0 ) (t,s,INF,0) (t,s,INF,0)(注意这里是原图的源汇点!不是附加的源汇点!)
这样以后,从 S S SS SS 到 T T TT TT 跑新图的最小费用最大流,再加上原图中每条边的下界流量乘以费用(必须跑的部分),就是最小费用可行流的费用了
就是消除下界影响后补流
代码:
#include <bits/stdc++.h>
using namespace std;
namespace IO {
inline char ch() {
static char buf[1 << 21], *p1 = buf, *p2 = buf;
return p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 1 << 21, stdin), p1 == p2)
? EOF
: *p1++;
}
inline int in() {
int s = 0, f = 1;
char x;
for (x = ch(); x < '0' || x > '9'; x = ch())
if (x == '-') f = -1;
for (; x >= '0' && x <= '9'; x = ch()) s = (s * 10) + (x & 15);
return f == 1 ? s : -s;
}
} // namespace IO
using namespace IO;
const int A = 4e5 + 5;
const int INF = 1e9;
int n;
int head[A], tot_road;
struct Road {
int nex, to, fw, cs;
} road[2 * A];
inline void edge(int x, int y, int z, int w) {
road[++tot_road] = {head[x], y, z, w};
head[x] = tot_road;
}
int d[A];
namespace NET {
#define S 0
#define T n + 1
int cur[A], h[A], tot = 1;
Road net[2 * A];
inline void ljb(int x, int y, int z, int w) {
net[++tot] = {cur[x], y, z, w};
cur[x] = tot;
net[++tot] = {cur[y], x, 0, -w};
cur[y] = tot;
}
int mf, mc;
inline void build() {
for (int i = 1; i <= n; i++) {
if (d[i] > 0) {
ljb(S, i, d[i], 0);
}
if (d[i] < 0) {
ljb(i, T, -d[i], 0);
}
for (int y = head[i]; y; y = road[y].nex) {
int z = road[y].to, c = road[y].cs;
ljb(i, z, INF, c);
}
ljb(i, 1, INF, 0);
}
return;
}
int dis[A], ex[A];
inline int spfa() {
queue<int> q;
for (int i = S; i <= T; i++) dis[i] = INF;
dis[S] = 0, ex[S] = 1;
q.push(S);
while (!q.empty()) {
int x = q.front();
q.pop();
ex[x] = 0;
for (int y = cur[x]; y; y = net[y].nex) {
int z = net[y].to, w = net[y].fw, c = net[y].cs;
if (w && dis[z] > dis[x] + c) {
dis[z] = dis[x] + c;
if (!ex[z]) {
q.push(z);
ex[z] = 1;
}
}
}
}
return dis[T] != INF;
}
inline int DFS(int x, int flow) {
if (x == T) {
mf += flow;
return flow;
}
ex[x] = 1;
int used = 0;
for (int &y = h[x]; y; y = net[y].nex) {
int z = net[y].to, w = net[y].fw, c = net[y].cs;
if (w && !ex[z] && dis[z] == dis[x] + c) {
int after = DFS(z, min(w, flow - used));
if (after) {
mc += after * c;
net[y].fw -= after;
net[y ^ 1].fw += after;
used += after;
}
}
if (used == flow) break;
}
ex[x] = 0;
return used;
}
inline void ISAP() {
mf = 0;
while (spfa()) {
for (int i = S; i <= T; i++) h[i] = cur[i];
DFS(S, INF);
}
return;
}
} // namespace NET
signed main() {
n = in();
for (int i = 1; i <= n; i++) {
int num = in();
for (int j = 1; j <= num; j++) {
int x = in(), y = in();
edge(i, x, 0, y);
NET::mc += y;
d[x]++, d[i]--;
}
}
NET::build();
NET::ISAP();
printf("%d\n", NET::mc);
return 0;
}