Projectors

题面

$n$ 个讲课, $m$ 个研讨会, $x$ 个高清投影仪和 $y$ 个普通投影仪。

每堂讲课必须使用一个高清投影仪,而研讨会可以使用普通或高清投影仪。

给定讲课和研讨会的时间区间,构造一个投影仪的分配方案。

$n,m,x,y \leq 300, a_i,b_i,p_i,q_i \leq 10^6$

题解

厉害的建模题。

分配普通投影仪的分配时不需要考虑每个讲课的区间具体是什么,只需要知道每个时刻被多少讲课所包含。以此为启发考虑到网络流。

先离散化时间,以时间轴为基础建图。我们考虑的是普通投影仪,就把其最多剩余量作为流量:

  • 经过 $i\rightarrow i+1$ 最多剩余 $\min(y,x+y-(\sigma_x+\sigma_y))$ 个普通投影仪,其中 $\sigma_x,\sigma_y$ 分别表示讲课数量和研讨会数量。那么就连一条 $(i,i+1)$ 流量为 $\min(y,x+y-(\sigma_x+\sigma_y))$ 的边。
  • 对于一个研讨会 $(p_i,q_i)$ ,连一条 $(p_i,q_i)$ 流量为 $1$ 的边。

这样建图,假设到了时间 $p_x$ ,如果流走了研讨会的边,说明此研讨会消耗了一个普通投影仪,如果走的是 $(p_x,p_x+1)$ 的边就是高清投影仪。

对于不合法,建图连边的时候若投影仪符合式子就不合法、最终流量不为 $y$ 不合法。

对于构造合法方案,开两个栈贪心即可。

代码

小问题

奇怪的是,网络流用邻接表会有错误(可能只是我的问题,还望指出),这个是错误代码:

const int N = 610 * 2;

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 {
	int n, m, x, y;
	int a[N], b[N];
	int sumX[N], sumY[N];
	int dtmp[N << 1], tot;

	int head[N << 1], cnt;
	struct Edge {
		int to; ll w; int op, nxt;
	} e[N * 5];
	void add_edge(int u, int v, ll w) {
		e[++cnt] = (Edge) {v, w, cnt ^ 1, head[u]}, head[u] = cnt;
		e[++cnt] = (Edge) {u, 0, cnt ^ 1, head[v]}, head[v] = cnt;
	}

	struct NetworkFlow {
		const int inf = 707406378;
		int S, T;
		int dis[N];
		queue <int> q;
		bool bfs() {
			while (!q.empty()) q.pop();
			memset (dis, 127 / 3, sizeof dis);
			dis[S] = 0;
			q.push(S);
			while (!q.empty()) {
				int u = q.front(); q.pop();
				for (int i = head[u]; i; i = e[i].nxt) {
					int v = e[i].to;
					if (dis[v] > dis[u] + 1 && e[i].w) {
						dis[v] = dis[u] + 1;
						if (v == T) return 1;
						q.push(v);
					}
				}
			}
			return 0;
		}
		ll dfs (int u, ll flow) {
			if (u == T) return flow;
			ll sum = 0;
			for (int i = head[u]; i; i = e[i].nxt) {
				int v = e[i].to;
				if (dis[v] == dis[u] + 1 && e[i].w) {
					ll f = dfs(v, min(e[i].w, flow - sum));
					if (!f) dis[v] = -1;
					e[i].w -= f;
					e[e[i].op].w += f;
					sum += f;
					if (sum == flow) break;
				}
			}
			return sum;
		}
		ll dinic () { ll ret = 0; for (; bfs(); ret += dfs(S, inf)); return ret; }
	} nf;

	vector <int> st[N], en[N];
	int id[N];
	int stX[N], stY[N];
	int Ans[N];

	void clr () {
		for (int i = 1; i <= tot; i++) {
			st[i].clear();
			en[i].clear();
		}
		tot = 0, cnt = 1;
		memset (Ans, 0, sizeof Ans);
		memset (head, 0, sizeof head);
		memset (sumX, 0, sizeof sumX);
		memset (sumY, 0, sizeof sumY);
	}

	int main () {
		for (int t = Read(); t--; clr()) {
			n = Read(), m = Read(), x = Read(), y = Read();
			for (int i = 1; i <= n; i++) a[i] = Read(), b[i] = Read(), dtmp[++tot] = a[i], dtmp[++tot] = b[i];
			for (int i = 1; i <= m; i++) a[i + n] = Read(), b[i + n] = Read(), dtmp[++tot] = a[i + n], dtmp[++tot] = b[i + n];
			sort (dtmp + 1, dtmp + 1 + tot);
			tot = unique (dtmp + 1, dtmp + 1 + tot) - dtmp - 1;
			for (int i = 1; i <= n + m; i++) {
				a[i] = lower_bound(dtmp + 1, dtmp + 1 + tot, a[i]) - dtmp;
				b[i] = lower_bound(dtmp + 1, dtmp + 1 + tot, b[i]) - dtmp;
				if (i <= n) sumX[a[i]]++, sumX[b[i]]--;
				else sumY[a[i]]++, sumY[b[i]]--;
				st[a[i]].emplace_back(i);
				en[b[i]].emplace_back(i);
			}
			for (int i = n + m; i >= n + 1; i--) {
				add_edge(a[i], b[i], 1);
				id[i] = cnt - 1;
			}
			bool flag = 1;
			for (int i = 1; i <= tot; i++) {
				sumX[i] += sumX[i - 1], sumY[i] += sumY[i - 1];
				if (i < tot)add_edge(i, i + 1, min(y, x + y - sumX[i] - sumY[i]));
				if (sumX[i] > x || x + y - (sumX[i] + sumY[i]) < 0) {
					puts("NO"); flag = 0; break;
				}
			}
			if (!flag) continue;
			nf.S = tot + 5, nf.T = nf.S + 1;
			add_edge(nf.S, 1, y);
			add_edge(tot, nf.T, nf.inf);
			if(nf.dinic() != y) { puts("NO"); continue; }
			puts("YES");
			int topX = 0, topY = 0;
			for (int i = 1; i <= x; i++) stX[++topX] = i;
			for (int i = x + 1; i <= x + y; i++) stY[++topY] = i;
			for (int i = 1; i <= tot; i++) {
				for (int j : en[i]) {
					if (Ans[j] <= x) stX[++topX] = Ans[j];
					else stY[++topY] = Ans[j];
				}
				for (int j : st[i]) {
					if (j <= n || e[id[j]].w) Ans[j] = stX[topX--];
					else Ans[j] = stY[topY--];
				}
			}
			for (int i = 1; i <= n + m; i++) printf ("%d ", Ans[i]); putchar(10);
		}
		return 0;
	}
}

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

AC 代码

下面是 AC 代码:

const int N = 2e4 + 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 {
	int n, m, x, y;
	int a[N], b[N];
	int sumX[N], sumY[N];
	int dtmp[N], tot;

	struct Edge {
		int to; ll w; int op;
	};
	vector <Edge> G[N];
	void add_edge(int u, int v, ll w) {
		G[u].emplace_back((Edge) {v, w, G[v].size()});
		G[v].emplace_back((Edge) {u, 0, G[u].size() - 1});
	}

	struct NetworkFlow {
		const int inf = 707406378;
		int S, T;
		int dis[N];
		queue <int> q;
		bool bfs() {
			while (!q.empty()) q.pop();
			memset (dis, 127 / 3, sizeof dis);
			dis[S] = 0;
			q.push(S);
			while (!q.empty()) {
				int u = q.front(); q.pop();
				for (auto i : G[u]) {
					int v = i.to;
					if (dis[v] > dis[u] + 1 && i.w) {
						dis[v] = dis[u] + 1;
						if (v == T) return 1;
						q.push(v);
					}
				}
			}
			return 0;
		}
		ll dfs (int u, ll flow) {
			if (u == T) return flow;
			ll sum = 0;
			for (int i = 0; i < G[u].size(); i++) {
				int v = G[u][i].to;
				if (dis[v] == dis[u] + 1 && G[u][i].w) {
					ll f = dfs(v, min(G[u][i].w, flow - sum));
					if (!f) dis[v] = -1;
					G[u][i].w -= f;
					G[v][G[u][i].op].w += f;
					sum += f;
					if (sum == flow) break;
				}
			}
			return sum;
		}
		ll dinic () { ll ret = 0; for (; bfs(); ret += dfs(S, inf)); return ret; }
	} nf;

	vector <int> st[N], en[N];
	int id[N];
	int stX[N], stY[N];
	int Ans[N];

	void clr () {
		for (int i = 1; i <= tot; i++) {
			st[i].clear();
			en[i].clear();
		}
		for (int i = 1; i <= n + m + 5; i++) G[i].clear();
		tot = 0;
		memset (Ans, 0, sizeof Ans);
		memset (sumX, 0, sizeof sumX);
		memset (sumY, 0, sizeof sumY);
	}

	int main () {
		for (int t = Read(); t--; clr()) {
			n = Read(), m = Read(), x = Read(), y = Read();
			for (int i = 1; i <= n; i++) a[i] = Read(), b[i] = Read(), dtmp[++tot] = a[i], dtmp[++tot] = b[i];
			for (int i = 1; i <= m; i++) a[i + n] = Read(), b[i + n] = Read(), dtmp[++tot] = a[i + n], dtmp[++tot] = b[i + n];
			sort (dtmp + 1, dtmp + 1 + tot);
			tot = unique (dtmp + 1, dtmp + 1 + tot) - dtmp - 1;
			for (int i = 1; i <= n + m; i++) {
				a[i] = lower_bound(dtmp + 1, dtmp + 1 + tot, a[i]) - dtmp;
				b[i] = lower_bound(dtmp + 1, dtmp + 1 + tot, b[i]) - dtmp;
				if (i <= n) sumX[a[i]]++, sumX[b[i]]--;
				else sumY[a[i]]++, sumY[b[i]]--;
				st[a[i]].emplace_back(i);
				en[b[i]].emplace_back(i);
			}
			for (int i = n + m; i >= n + 1; i--) {
				add_edge(a[i], b[i], 1);
				id[i] = G[a[i]].size() - 1;
			}
			bool flag = 1;
			for (int i = 1; i <= tot; i++) {
				sumX[i] += sumX[i - 1], sumY[i] += sumY[i - 1];
				if (i < tot)add_edge(i, i + 1, min(y, x + y - sumX[i] - sumY[i]));
				if (sumX[i] > x || x + y - (sumX[i] + sumY[i]) < 0) {
					puts("NO"); flag = 0; break;
				}
			}
			if (!flag) continue;
			nf.S = tot + 2, nf.T = nf.S + 1;
			add_edge(nf.S, 1, y);
			add_edge(tot, nf.T, nf.inf);
			if(nf.dinic() != y) { puts("NO"); continue; }
			puts("YES");
			int topX = 0, topY = 0;
			for (int i = 1; i <= x; i++) stX[++topX] = i;
			for (int i = x + 1; i <= x + y; i++) stY[++topY] = i;
			for (int i = 1; i <= tot; i++) {
				for (int j : en[i]) {
					if (Ans[j] <= x) stX[++topX] = Ans[j];
					else stY[++topY] = Ans[j];
				}
				for (int j : st[i]) {
					if (j <= n || G[a[j]][id[j]].w) Ans[j] = stX[topX--];
					else Ans[j] = stY[topY--];
				}
			}
			for (int i = 1; i <= n + m; i++) printf ("%d ", Ans[i]); putchar(10);
		}
		return 0;
	}
}

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

评论

暂无评论

发表评论

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

博客信息

作者
Jayun
时间
2024-02-19 20:59:03
博客类型