luogu4382 && bzoj5251[八省联考2018]劈配

[八省联考2018]劈配

luogu
bzoj

分析

头一回见到Dinic可以动态加边的。
残量网络真是一个神奇的东西。
如果想到了动态加边,那就是一个简单的匹配问题了。
源点连导师,学员连汇点。加边的时候按志愿加,把每个志愿加进去之后跑 D i n i c Dinic Dinic B f s Bfs Bfs分层部分。如果有增广路就增广一下然后 b r e a k break break,否则的话把边删掉。
这样第一问就搞定了。
第二问的话,考虑把每个人的增广状态的图记住。然后二分地把这个人插入某个状态的图中,不用增广,直接 B f s Bfs Bfs判断有没有增广路即可。(记得删边)
复杂度 O ( 能 过 ) O(能过) O()

代码

#include<bits/stdc++.h>
const int M = 5e3 + 10, N = 4e2 + 10, inf = 0x3f3f3f3f;
int ri() {
	char c = getchar(); int x = 0, f = 1; for(;c < '0' || c > '9'; c = getchar()) if(c == '-') f = -1;
	for(;c >= '0' && c <= '9'; c = getchar()) x = (x << 1) + (x << 3) - '0' + c; return x * f;
}
int n, m, s, t, tm, lim[N], vis[N], q[N], D[N], Ans[N];
struct NetWork {
	int nx[M], pr[N], to[M], w[M], tp;
	/*void operator = (const NetWork &a) {
		tp = a.tp;
		memcpy(pr + 1, a.pr + 1, sizeof(int) * t);
		memcpy(to + 1, a.to + 1, sizeof(int) * tp);
		memcpy(w + 1, a.w + 1, sizeof(int) * tp);
		memcpy(nx + 1, a.nx + 1, sizeof(int) * tp);
	}*/
	void clear() {tp = 1; memset(pr + 1, 0, sizeof(int) * t);}
	void ins(int u, int v, int _w) {to[++tp] = v; nx[tp] = pr[u]; pr[u] = tp; w[tp] = _w;}
	void add(int u, int v, int w) {ins(u, v, w); ins(v, u, 0);}
	void del() {
		pr[to[tp ^ 1]] = nx[tp]; pr[to[tp]] = nx[tp ^ 1];
		tp -= 2;
	}
	bool Bfs() {
		vis[s] = ++tm; D[s] = 1; int L = 0, R = 0;
		for(int u = s;vis[t] != tm && L <= R; u = q[++L])
			for(int i = pr[u]; i; i = nx[i])
				if(w[i] && vis[to[i]] != tm)
					q[++R] = to[i], vis[to[i]] = tm, D[to[i]] = D[u] + 1;
		return vis[t] == tm;
	}
	int Dfs(int u, int minf) {
		if(u == t) return minf;
		if(D[u] >= D[t]) return D[u] = 0;
		for(int i = pr[u], f; i; i = nx[i])
			if(w[i] && D[to[i]] == D[u] + 1 && (f = Dfs(to[i], std::min(minf, w[i]))))
				return w[i] -= f, w[i ^ 1] += f, f;
		return D[u] = 0; 
	}
}G[N];
std::vector<int>v[N][N];
bool Check(int x, int u) {
	bool flag = false;
	for(int j = 1;j <= lim[u] && !flag; ++j) 
	if(v[u][j].size()) {
		for(int k : v[u][j])
			G[x - 1].add(k, u + m, 1);
		if(G[x - 1].Bfs()) flag = true;
		for(int k = v[u][j].size();k--;)
			G[x - 1].del();
	}
	return flag;
}
int Query(int i) {
	int L = 1, R = i - 1, ans = i;
	for(;L <= R;) {
		int m = L + R >> 1;
		if(Check(m, i)) ans = i - m, L = m + 1;
		else R = m - 1;
	}
	return ans;
}
int main() {
	int T = ri(); ri();
	for(;T--;) {
		n = ri(); m = ri(); s = n + m + 1; t = s + 1;
		G[0].clear();
		for(int i = 1; i <= n; ++i)
			for(int j = 1;j <= m; ++j)
				v[i][j].clear();
		for(int i = 1;i <= m; ++i)
			G[0].add(s, i, ri());
		for(int i = 1;i <= n; ++i)
			G[0].add(i + m, t, 1);
		for(int i = 1;i <= n; ++i) {
			Ans[i] = m + 1;
			G[i] = G[i - 1];
			for(int j = 1;j <= m; ++j) {
				int x = ri();
				if(x) v[i][x].push_back(j);
			}
			for(int j = 1;j <= m; ++j)
				if(v[i][j].size()) {
					for(int k : v[i][j])
						G[i].add(k, i + m, 1);
					if(G[i].Bfs()) {
						G[i].Dfs(s, inf);
						Ans[i] = j;
						break;
					}
					for(int k = v[i][j].size();k--;)
						G[i].del();
				}
		}
		for(int i = 1;i <= n; ++i) 
			printf("%d ", Ans[i]);
		puts("");
		for(int i = 1;i <= n; ++i)
			lim[i] = ri();
		for(int i = 1;i <= n; ++i) {
			if(Ans[i] <= lim[i]) printf("0 ");
			else printf("%d ", Query(i));
		}
		puts("");
	}
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值