[CEOI2008]order

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;
} 
posted @ 2021-01-16 16:30  Spring-Araki  阅读(62)  评论(0编辑  收藏  举报