[CEOI2008]order
明显的最大权闭合图,唯一有所不同的就只有租这种迷惑操作了。
题目分析:
采用模板化的建图方式:
-
首先,因为第 \(i\) 个任务的收入相当于正边权,所以连由源点 \(s\) 到任务 \(i\) 的容量为收入 \(xi\) 的有向边;
-
然后,先不考虑购买操作,假定只能租,那问题就很简单了,直接连一条由任务 \(i\) 到机器 \(b\) 的容量为其费用 \(b\) 的有向边;
-
最后,考虑购买操作会对结果产生什么影响。可以发现,对于第 \(i\) 台机器而言,当且仅当前面所有租用第 \(i\) 机器产生的总费用大于购买机器的费用 \(c\) 时,我们才会选择购买机器。于是就可以利用网络流的特性,将第 \(i\) 台机器向汇点 \(t\) 连一条容量为 \(c\) 的有向边。因为,第一,费用相当于负权,应该连向汇点;其次,这样连边可以限制当前机器的费用(如果总租金小于购买费用,选择租;如果大于了购买费用,当前边的容量会限制流量,将费用变为购买值)。
最后答案就是所有任务总收入减去最小割的值。剩下的都是板子啦~~
完整代码:
#include<cstdio>
#include<queue>
#include<cstring>
#include<algorithm>
using namespace std;
const int N = 2420, M = 3e6, inf = 1e9;
int n,m,s,t,tot = -1,head[N],cur[N],dep[N],f[M],sum,q[M],top,ed;
struct node {
int to,nex;
}e[M];
inline void add(int x,int y,int w) {
e[++tot] = (node) {y,head[x]}, f[tot] = w, head[x] = tot;
e[++tot] = (node) {x,head[y]}, head[y] = tot;
}
inline bool bfs() {
memset(dep,-1,sizeof(dep));
dep[s] = 0, cur[s] = head[s], q[top = 1] = s; ed = 1;
int now,ver;
while(top <= ed) {
now = q[top++];
for(register int i = head[now]; ~i; i = e[i].nex) {
ver = e[i].to;
if(dep[ver] == -1 && f[i]) {
dep[ver] = dep[now] + 1, cur[ver] = head[ver];
if(ver == t) return 1;
q[++ed] = ver;
}
}
}
return 0;
}
inline int find(int x,int limit) {
if(x == t) return limit;
int flow = 0, tmp, ver;
for(register int i = head[x]; ~i && flow < limit; i = e[i].nex) {
ver = e[i].to;
if(dep[ver] == dep[x] + 1 && f[i]) {
tmp = find(ver,min(limit - flow,f[i]));
if(!tmp) dep[ver] = -1;
f[i] -= tmp, f[i ^ 1] += tmp, flow += tmp;
}
}
return flow;
}
inline int dinic() {
int res = 0, flow;
while(bfs()) res += find(s,inf);
return res;
}
inline void read(int &x) {
x = 0;int ff = 1;
char s = getchar();
while(s < '0' || s > '9') {
if(s == '-') ff = -1;
s = getchar();
}
while(s <= '9' && s >= '0') {x = x * 10 + s - '0', s = getchar(); }
x *= ff;
}
signed main() {
memset(head,-1,sizeof(head));
read(n), read(m);
int a,b,xi,ti;
s = 0, t = n + m + 1;
for(register int i = 1; i <= n; i ++) {
read(xi), read(ti);
sum += xi, add(s,i,xi);
while(ti --) read(a), read(b), add(i,a + n,b);
}
for(register int i = 1; i <= m; i ++) read(a), add(i + n,t,a);
printf("%d",sum - dinic());
return 0;
}