「Codeforces1250K」Projectors-网络流

Description

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

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

i i i堂讲课时间在 [ a i , b i ) [a_i,b_i) [ai,bi),第 i i i次研讨会在时间在 [ p i , q i ) [p_i,q_i) [pi,qi),一个投影仪一次只能用在一个地方,且在使用完毕后才会归还。

构造为每节讲课/研讨会分配一个投影仪的方案。

n , m , x , y ≤ 300 , a i , b i , p i , q i ≤ 1 0 6 n,m,x,y \leq 300, a_i,b_i,p_i,q_i \leq 10^6 n,m,x,y300,ai,bi,pi,qi106

Solution

考虑已经知道了普通投影仪如何分配,那么存在分配高清投影仪的合法方案的充要条件是,对于任意整数 t t t,时刻 t + 0.5 t+0.5 t+0.5满足需要用到的投影仪数量不超过 x + y x+y x+y

那么这意味着,我们只关心每个时刻到底要用多少投影仪,而不关心他们具体是什么。这启发我们想到网络流, 1 1 1单位流量代表普通投影仪在每个时刻的状态。考虑每个时刻最多有多少普通投影仪是空闲的,显然是 m i n ( x , x + y − s u m ) min(x,x+y-sum) min(x,x+ysum),其中 s u m sum sum是这个时刻所需要的投影仪,即此时至少有 y − m i n ( x , x + y − s u m ) y-min(x,x+y-sum) ymin(x,x+ysum)台投影仪被使用。

  • 建立一个时间轴,上面的节点依次表示时刻 0 , 1 , . . . , i , . . . 0,1,...,i,... 0,1,...,i,... i + 0.5 i+0.5 i+0.5 i + 1.5 i+1.5 i+1.5连流量为 m i n ( x , x + y − s u m ) min(x,x+y-sum) min(x,x+ysum)的边,其中 s u m sum sum是时刻 i + 0.5 i+0.5 i+0.5所需要的投影仪。时间轴上的流量表示空闲。
  • 对于研讨会 i i i,连边 p i → q i p_i\to q_i piqi,流量为 1 1 1。表示这个投影仪此时被次研讨会使用。

存在合法解的方案当且仅当 0 0 0 I n f Inf Inf的流量为 y y y

如何构造方案?考虑跑完最大流后,每次从 t t t s s s退 1 1 1流量,如果一次研讨会使用当前的投影仪,当前仅当这个研讨会所代表的边被这个流量经过。

分配高清投影仪贪心即可。总时间复杂度 O ( T ( n + m ) 2 ) O(T(n+m)^2) O(T(n+m)2)

tips: 需要把时间离散保证复杂度。

#include <bits/stdc++.h>
using namespace std;

const int maxn = 605, Inf = 1 << 30;

inline int gi()
{
	char c = getchar();
	while (c < '0' || c > '9') c = getchar();
	int sum = 0;
	while ('0' <= c && c <= '9') sum = sum * 10 + c - 48, c = getchar();
	return sum;
}

int n, m, A, B, x[maxn], y[maxn], *q[maxn << 1], ti;
int vis[maxn], now[maxn], ans[maxn], sum[maxn << 1], Id[maxn];
vector<int> vec[maxn << 1];

struct edge
{
	int to, next, cap;
} e[maxn * 5];
int h[maxn << 1], cur[maxn << 1], dis[maxn << 1], tot;

inline void add(int u, int v, int w)
{
	//printf("%d %d %d\n", u, v, w);
	e[++tot] = (edge) {v, h[u], w}; h[u] = tot;
	e[++tot] = (edge) {u, h[v], 0}; h[v] = tot;
}

bool bfs(int s, int t, int E)
{
	static int u, l, r, q[maxn << 1];
	memset(dis + 1, 0, sizeof(int) * ti);
	l = 0; q[r = 1] = s; dis[s] = 1;
	while (l < r) {
		u = q[++l];
		for (int i = h[u], v; v = e[i].to, i; i = e[i].next)
			if ((i & 1) != E && !dis[v] && e[i].cap) dis[v] = dis[u] + 1, q[++r] = v;
	}
	return dis[t];
}

int dfs(int u, int t, int a, int E)
{
	if (u == t || !a) return a;
	int flow = 0, f;
	for (int &i = cur[u], v; v = e[i].to, i; i = e[i].next)
		if ((i & 1) != E && dis[v] == dis[u] + 1 && (f = dfs(v, t, min(a, e[i].cap), E))) {
			a -= f; flow += f;
			e[i].cap -= f; e[i ^ 1].cap += f;
			if (!a) break;
		}
	return flow;
}

int max_flow(int s, int t, int f, int E)
{
	int res = 0;
	while (bfs(s, t, E) && res < f) {
		memcpy(cur + 1, h + 1, sizeof(int) * ti);
		res += dfs(s, t, f, E);
	}
	return res;
}

int main()
{
	freopen("arrange20.in", "r", stdin);
	freopen("B.out", "w", stdout);

	int T = gi();
	while (T--) {
		n = gi(); m = gi(); A = gi(); B = gi();

		int Min = -1e9, Max = 1e9 + 1;
		q[tot = 1] = &Min; q[++tot] = &Max;
		for (int i = 1; i <= n; ++i) x[i] = gi(), y[i] = gi(), q[++tot] = x + i, q[++tot] = y + i;
		for (int i = 1; i <= m; ++i) x[n + i] = gi(), y[n + i] = gi(), q[++tot] = x + n + i, q[++tot] = y + n + i;
		sort(q + 1, q + tot + 1, [](int *a, int *b) {return *a < *b;});
		ti = 0;
		for (int lst = -1, i = 1; i <= tot; ++i) {
			if (*q[i] == lst) *q[i] = ti;
			else lst = *q[i], *q[i] = ++ti;
		}
		
		memset(sum + 1, 0, sizeof(int) * ti);
		for (int i = 1; i <= n + m; ++i)
			for (int j = x[i]; j < y[i]; ++j) ++sum[j];
		
		tot = 1; memset(h + 1, -1, sizeof(int) * ti);
		bool flg = 1;
		for (int i = 1; i < ti; ++i) {
			if (A + B < sum[i]) {flg = 0; break;}
			add(i, i + 1, min(B, A + B - sum[i]));
		}
		if (!flg) {puts("NO"); continue;}
		for (int i = n + 1; i <= n + m; ++i) add(x[i], y[i], 1), Id[i - n] = tot;

		if (max_flow(1, ti, Inf, -1) != B) puts("NO");
		else {
			puts("YES");
			memset(vis + 1, 0, sizeof(int) * m);
			memset(ans + 1, 0, sizeof(int) * (n + m));
			for (int i = 1; i <= m; ++i) vis[i] = !e[Id[i]].cap;
			for (int i = 1; i <= B; ++i) {
				max_flow(ti, 1, 1, 0);
				for (int j = 1; j <= m; ++j)
					if (!vis[j] && !e[Id[j]].cap) vis[j] = 1, ans[n + j] = A + i;
			}
			for (int i = 1; i <= ti; ++i) vec[i].clear();
			for (int i = 1; i <= n + m; ++i) if (!ans[i]) vec[x[i]].push_back(i);
			memset(now + 1, 0, sizeof(int) * A);
			for (int i = 1; i <= ti; ++i)
				for (int t : vec[i])
					for (int j = 1; j <= A; ++j)
						if (y[now[j]] <= x[t]) {now[j] = t; ans[t] = j; break;}
			for (int i = 1; i <= n; ++i) printf("%d ", ans[i]), assert(ans[i]);
			puts("");
			for (int i = 1; i <= m; ++i) printf("%d ", ans[n + i]), assert(ans[n + i]);
			puts("");
		}
	}
	
	return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值