[ZJOI2013] 防守战线

题目描述

战线可以看作一个长度为 $n$ 的序列,现在需要在这个序列上建塔来防守敌兵,在序列第 $i$ 号位置上建一座塔有 $C_i$ 的花费,且一个位置可以建任意多的塔,费用累加计算。有 $m$ 个区间 $[L_1, R_1], [L_2, R_2], \cdots, [L_m, R_m]$ ,在第 $i$ 个区间的范围内要建至少 $D_i$ 座塔。求最少花费。

$n\le 1000$ $m\le 10000$ $1\le L_i\le R_i\le n$ ,其余数据均 $\le 10000$

题解

$p_i$ 表示前 $i$ 个位置建的数量,则有:

$$\min\left\{\sum(p_i-p_{i-1})C_i~|~p_{R_i}-p_{L_i-1}\geq D_i,p_i-p_{i-1}\ge0\right\} $$

$$\min\left\{\sum p_i(C_i-C_{i-1})~|~p_{R_i}-p_{L_i-1}\geq D_i,p_i-p_{i-1}\ge0\right\} $$

到这里式子推导有一步很妙的步骤:考虑后面两个条件不等式与前面合并,发现这是在 $\min$ 里面,如果不合法(即 $p_{R_i}-p_{L_i-1}<D_i$ $p_i-p_{i-1}<0$ ),就让它取无穷大,让无穷来限定不合法方案:

$$\min\left\{\sum p_i(C_i-C_{i+1})+\sum\infty\max(D_i-p_{R_i}+p_{L_i-1},0)+\sum\infty\max(p_{i-1}-p_i,0)\right\} $$

然后发现这是费用流模型的对偶,直接费用流做。

代码

90pts

人傻常数大,自己写的费用流 90pts。

const int N = 1e4 + 10, M = 3e4 + 5;

inline ll Read() {
	ll x = 0, f = 1;
	char c = getchar();
	while (c != '-' && (c < '0' || c > '9')) c = getchar();
	if (c == '-') f = -f, c = getchar();
	while (c >= '0' && c <= '9') x = (x << 3) + (x << 1) + c - '0', c = getchar();
	return x * f;
}

namespace Main {
	const int inf = 707406378;
	struct NetworkFlow {
		int S, T, n;
		int head[N], tot = 1;
		struct Edge {
			int to; int w, c; int nxt;
		} e[M << 1];
		void add_edge(int u, int v, int w, int c) {
			e[++tot] = (Edge) {v, w, c, head[u]}, head[u] = tot;
			e[++tot] = (Edge) {u, 0, -c, head[v]}, head[v] = tot;
		}
		NetworkFlow() {tot = 1;}

		bool vis[N];
		int dis[N];
		deque <int> q;
		bool spfa() {
			while (!q.empty()) q.pop_back();
			for (int i = 1; i <= n; i++) vis[i] = 0, dis[i] = inf;
			dis[T] = 0, vis[T] = 1;
			q.push_back(T);
			while (!q.empty()) {
				int u = q.front(); q.pop_front(); vis[u] = 0;
				for (int i = head[u]; i; i = e[i].nxt) {
					int v = e[i].to;
					if (dis[v] > dis[u] - e[i].c && e[i ^ 1].w) {
						dis[v] = dis[u] - e[i].c;
						if (!vis[v]) {
							vis[v] = 1;
							if (q.size() && dis[v] < dis[q.front()]) q.push_front(v);
							else q.push_back(v);
						}
					}
				}
			}
			return dis[S] < inf;
		}
		int mcost = 0;
		int dfs (int u, int flow) {
			vis[u] = 1;
			if (u == T || !flow) return flow;
			int sum = 0;
			for (int i = head[u]; i; i = e[i].nxt) {
				int v = e[i].to;
				if (dis[v] == dis[u] - e[i].c && e[i].w && !vis[v]) {
					int f = dfs(v, min(e[i].w, flow - sum));
					if (!f) continue;
					e[i].w -= f;
					e[i ^ 1].w += f;
					sum += f;
					mcost += f * e[i].c;
					if (sum == flow) break;
				}
			}
			return sum;
		}
		int mcmf () { 
			int ret = 0; 
			while (spfa()) {
				vis[T] = 1;
				for (; vis[T]; ret += dfs(S, inf))
					for (int i = 1; i <= n; i++) vis[i] = 0;
			} 
			return ret; 
		}
	} nf;
	int n, m;
	int c[N];
	int main () {
		n = Read(), m = Read();
		nf.S = n + 2, nf.n = nf.T = n + 3;
		for (int i = 1; i <= n; i++) nf.add_edge(i + 1, i, inf, 0);
		for (int i = 1; i <= n; i++) c[i] = Read();
		for (int i = 0; i <= n; i++) {
			if (c[i] - c[i + 1] > 0) nf.add_edge(nf.S, i + 1, c[i] - c[i + 1], 0);
			else nf.add_edge(i + 1, nf.T, c[i + 1] - c[i], 0);
		}
		for (int i = 1; i <= m; i++) {
			int l = Read(), r = Read(); int w = Read();
			nf.add_edge(r + 1, l, inf, -w);
		}
		nf.mcmf();
		printf ("%d\n", -nf.mcost);
		return 0;
	}
}

int main () {
//	freopen(".in", "r", stdin);
//	freopen(".out", "w", stdout);
	Main::main();
	return 0;
}

100pts

看了提交记录里一位大佬跑得飞快的代码,抄了些优化,AC 了。记记卡常和优化方法。

const int N = 1e3 + 10, M = 3e4 + 5;

inline ll Read() {
	ll x = 0, f = 1;
	char c = getchar();
	while (c != '-' && (c < '0' || c > '9')) c = getchar();
	if (c == '-') f = -f, c = getchar();
	while (c >= '0' && c <= '9') x = (x << 3) + (x << 1) + c - '0', c = getchar();
	return x * f;
}

namespace Main {
	const int inf = 707406378;
	struct NetworkFlow {
		int S, T, n;
		int head[N], tot = 1;
		int cur[N];
		struct Edge {
			int to; int w, c; int nxt;
		} e[M << 1];
		void add_edge(int u, int v, int w, int c) {
			e[++tot] = (Edge) {v, w, c, head[u]}, head[u] = tot;
			e[++tot] = (Edge) {u, 0, -c, head[v]}, head[v] = tot;
		}
		NetworkFlow() {tot = 1;}

		bool vis[N], in[N];
		int dis[N], de[N];
		int q[M << 4], bk, ft;
		void spfa() {
			for (int i = 1; i <= n; i++) vis[i] = 0, dis[i] = inf;
			dis[S] = 0, vis[S] = 1;
			q[ft = bk = 1] = S;
			while (ft <= bk) {
				int u = q[ft]; ft++; vis[u] = 0;
				for (int i = head[u]; i; i = e[i].nxt) {
					int v = e[i].to;
					if (dis[v] > dis[u] + e[i].c && e[i].w) {
						dis[v] = dis[u] + e[i].c;
						if (!vis[v]) {
							vis[v] = 1;
							q[++bk] = v;
						}
					}
				}
			}
		}
		int mcost = 0;
		int dfs (int u, int flow) {
			if (u == T || !flow) {mcost += dis[u] * flow; return flow;}
			vis[u] = in[u] = 1;
			int sum = 0;
			for (int &i = cur[u]; i; i = e[i].nxt) {
				int v = e[i].to;
				if (!e[i].w) continue;
				int d = dis[u] + e[i].c - dis[v];
				de[v] = min(de[v], d);
				if (d == 0 && !in[v]) {
					int f = dfs(v, min(e[i].w, flow - sum));
					if (!f) continue;
					e[i].w -= f;
					e[i ^ 1].w += f;
					sum += f;
					if (sum == flow) break;
				}
			}
			in[u] = 0;
			return sum;
		}
		int mcmf () { 
			int ret = 0;
			spfa();
			while (1) {
				for (int i = 1; i <= n; i++) vis[i] = 0, de[i] = inf, cur[i] = head[i];
				ret += dfs (S, inf);
				int tmp = inf;
				for (int i = 1; i <= n; i++) if (!vis[i]) tmp = min(tmp, de[i]);
				if (tmp == inf) break;
				for (int i = 1; i <= n; i++) if (!vis[i]) dis[i] += tmp;
			} 
			return ret; 
		}
	} nf;
	int n, m;
	int c[N];
	int main () {
		n = Read(), m = Read();
		nf.S = n + 2, nf.n = nf.T = n + 3;
		for (int i = 1; i <= n; i++) nf.add_edge(i + 1, i, inf, 0);
		for (int i = 1; i <= n; i++) c[i] = Read();
		for (int i = 0; i <= n; i++) {
			if (c[i] - c[i + 1] > 0) nf.add_edge(nf.S, i + 1, c[i] - c[i + 1], 0);
			else nf.add_edge(i + 1, nf.T, c[i + 1] - c[i], 0);
		}
		for (int i = 1; i <= m; i++) {
			int l = Read(), r = Read(); int w = Read();
			nf.add_edge(r + 1, l, inf, -w);
		}
		nf.mcmf();
		printf ("%d\n", -nf.mcost);
		return 0;
	}
}

int main () {
//	freopen(".in", "r", stdin);
//	freopen(".out", "w", stdout);
	Main::main();
	return 0;
}
EOF

评论

暂无评论

发表评论

可以用@mike来提到mike这个用户,mike会被高亮显示。如果你真的想打“@”这个字符,请用“@@”。

博客信息

作者
Jayun
时间
2023-10-27 11:47:36
博客类型