洛谷 P4258 UOJ #171 【WC2016】挑战NPC

题解

建模:

把每个框子拆成三个点并连在一起,每颗球向他能匹配的框子的三个点分别连边。
之后求一般图最大匹配即可

证明:

如果原问题取得最优解,每颗球都会与一个框匹配,每个框如果匹配的球数不超过两个,则内部产生一条匹配边,这是一个最大匹配。
反过来,任意最大匹配中,每颗球必然能匹配,而后框子的“内部匹配边”会尽可能多,所以任意最大匹配一定是如上所述的一个匹配。

代码

#include<bits/stdc++.h>

using namespace std;

const int maxm = 100 + 5;
const int maxn = maxm * 6;
const int maxe = 500000 + 5;

int t;
int n, m, e;
int head[maxn], nxt[maxe * 2], to[maxe * 2], cnt;
void inline addEdge(int u, int v) {
	nxt[++cnt] = head[u], to[cnt] = v, head[u] = cnt;
	nxt[++cnt] = head[v], to[cnt] = u, head[v] = cnt;
}

namespace Blossom
{
	int pre[maxn];
	int find(int x) { return pre[x] ? pre[x] = find(pre[x]) : x; }
	int type[maxn], que[maxn], front, tail, link[maxn], match[maxn], deep[maxn], fa[maxn];
	
	int inline lca(int u, int v) {
		u = find(u), v = find(v);
		while(u != v) {
			if(deep[u] > deep[v]) u = find(fa[u]);
			else v = find(fa[v]);
		}
		return u;
	}
	
	void inline shrink(int u, int v, int e) {
		while(find(u) != e) {
			link[u] = v, v = match[u];
			if(type[v] == 2) type[v] = 1, que[tail++] = v;
			if(find(u) == u) pre[u] = e;
			if(find(v) == v) pre[v] = e;
			u = link[v];
		}
	}
	
	bool inline bfs(int s)
	{
		memset(pre, 0, sizeof pre); 
		memset(type, 0, sizeof type); 
		memset(link, 0, sizeof fa);
		memset(fa, 0, sizeof fa);
		front = 0, tail = 0;
		type[que[tail++] = s] = 1;
		deep[s] = 1; fa[s] = 0;
		while(front != tail) 
		{
			int u = que[front++];
			for(int e = head[u]; e; e = nxt[e]) 
			{
				int v = to[e];
				if(find(v) == find(u) || type[v] == 2) continue;
				if(!type[v]) {
					deep[v] = deep[u] + 1, fa[v] = u;
					type[v] = 2, link[v] = u;
					if(!match[v]) {
						for(int now = v, last; now; now = last) {
							last = match[link[now]];
							match[now] = link[now], match[link[now]] = now;
						}
						return true;
					}
					type[match[v]] = 1, que[tail++] = match[v]; deep[match[v]] = deep[v] + 1, fa[match[v]] = v;
				} else if(type[v] == 1){
					int e = lca(u, v);
					shrink(u, v, e);
					shrink(v, u, e);
				}
			}
		}
		
		return false;
	}
	
	int inline edmond() {
		int ans = 0;
		memset(match, 0, sizeof match);
		for(int i = 1; i <= n + 3 * m; ++i) if(!match[i]) ans += bfs(i);
		return ans;
	}
}

using namespace Blossom;

void inline Init()
{
	memset(head, 0, sizeof head);
	memset(to, 0, sizeof to);
	memset(nxt, 0, sizeof nxt);
	cnt = 0;
	scanf("%d %d %d", &n, &m, &e);
	int u, v;
	while(e--) {
		scanf("%d %d", &u, &v);
		addEdge(u, 3 * (v - 1) + n + 1);
		addEdge(u, 3 * (v - 1) + n + 2);
		addEdge(u, 3 * (v - 1) + n + 3);
	}
	for(int i = 1; i <= m; ++i) {
		int a = 3 * (i - 1) + n + 1, b = 3 * (i - 1) + n + 2, c = 3 * (i - 1) + n + 3;
		addEdge(a, b);
		addEdge(b, c);
		addEdge(c, a);
	}
}

int main()
{
	scanf("%d\n", &t);
	while(t--) {
		Init();
		printf("%d\n", edmond() - n);
		for(int i = 1; i <= n; ++i) printf("%d ", (match[i] - n - 1) / 3 + 1);
		putchar('\n');
	}
	return 0;
}
posted @ 2018-12-23 23:27  cjrsacred  阅读(89)  评论(0编辑  收藏  举报