[Link]
给定一个二分图,问你哪些边永远不可能成为最大匹配的边。
比赛的时候全败在题上了……
这道题我见过三次,第一次在上一年训练的时候见到,随便YY一下全手写花了不到1小时就过了;第二次在多校见少考虑了一种情况WA全场;第三次,hopcroft karp模板右边不能匹配居然match2不是-1而是n,直接又WA全场,把-1改成n立马过了。我简直不能再蠢……
实际这题还是很简单的(多校那个解题报告的解法要加各种边还得匹配两次感觉很不科学……)
做法就是先匹配,然后匹配边先排除这是肯定的。
然后匹配边从右往左连,非匹配边从左往右连。做强连通。
最后如果一条边符合下面三种情况之一就是可以作为匹配边的。
-
这个的意思是要连的边两个点在同一个强连通分量。具体换边的方法就是沿着到彼此的路径交替互换就行了。 -
左边的点被左边一个未被匹配的空点可达,换边的方法就是从空点一直走过来,交替互换。 -
右边的点可达一个右边未被匹配的空点,换边的方法就是沿着这条路径一直走到空点,然后交替互换。
所以思路就明确了,SCC以后判连通性即可。
本来模块不能外传……但是想到我们的模块已经落后别人10条街了,而且网络赛本来就公开了代码……
#define rep(i,n) for (int i = 0; i < (int)(n); i++) #define foreach(it,v) for (__typeof((v).end()) it = (v).begin(); it != (v).end(); it++) typedef pair <int, int> PII; typedef long long ll; const int MAXN = 100005; int n, m, e; int match1[MAXN], match2[MAXN]; int Q[MAXN], D1[MAXN], D2[MAXN]; vector <int> E[MAXN]; inline bool bfs() { int s = 0, t = 0, u, v; memset(D1, -1, sizeof(D1)); memset(D2, -1, sizeof(D2)); for (int i = 0; i < n; i++) if (match1[i] == -1) Q[t++] = i, D1[i] = 0; while (s != t) if ((u = Q[s++]) != n) for (int i = 0; i < (int)E[u].size(); i++) if (D2[v = E[u][i]] == -1) { D2[v] = D1[u] + 1; if (D1[match2[v]] == -1) D1[Q[t++] = match2[v]] = D2[v] + 1; } return D1[n] != -1; } bool dfs(int u) { for (int i = 0, v; i < (int)E[u].size(); i++) if (D2[v = E[u][i]] == D1[u] + 1 && (D2[v] = -1) && (match2[v] == n || dfs(match2[v]))) { match1[u] = v; match2[v] = u; D1[u] = -1; return true; } D1[u] = -1; return false; } inline int hopcroft_karp() { memset(match1, -1, sizeof(match1)); for (int i = 0; i < m; i++) match2[i] = n; int ret = 0; for (int i = 0; i < n; i++) for (int j = 0, u; j < (int)E[i].size(); j++) if (match2[u = E[i][j]] == n) { match1[i] = u; match2[u] = i; ret++; break; } while (bfs()) for (int i = 0; i < n; i++) if (match1[i] == -1 && dfs(i)) ret++; return ret; } int toNum(char c) { if (c >= '0' && c <= '9') return c - '0'; return c - 'A' + 10; } int trans(char *s) { return toNum(s[0]) * 32 * 32 + toNum(s[1]) * 32 + toNum(s[2]); } struct SCC_T { int n; int dfn[MAXN], low[MAXN], id[MAXN], num, st[MAXN], top, in[MAXN], tot; vector <int> E[MAXN]; int come[MAXN], go[MAXN]; void clear(int _n) { n = _n; rep (i, n) E[i].clear(); memset(dfn, 0, sizeof(int) * n); memset(low, 0, sizeof(int) * n); memset(in, 0, sizeof(int) * n); memset(id, 0xff, sizeof(int) * n); memset(come, 0, sizeof(int) * n); memset(go, 0, sizeof(int) * n); } void addedge(int x, int y) { E[x].push_back(y); } void tarjan(int now){ in[st[top++] = now] = true; dfn[now] = low[now] = ++tot; int i; for (int ii = E[now].size() - 1; ii >= 0; --ii){ i = E[now][ii]; if (!dfn[i]){ tarjan(i); low[now] = min(low[now], low[i]); } else if (in[i]) low[now] = min(low[now], dfn[i]); } if (dfn[now] == low[now]){ do{ i = st[--top]; in[i] = false; id[i] = num; }while (i != now); ++num; } } void gao() { for (int i = top = num = tot = 0; i < n; ++i) if (!dfn[i]) tarjan(i); } void bfs(int (&vis)[MAXN], const vector <int> (&E)[MAXN]) { static int Q[MAXN]; int qh = 0, qt = 0; rep (i, n) if (vis[i]) Q[qt++] = i; while (qh < qt) { int u = Q[qh++]; rep (i, E[u].size()) { int v = E[u][i]; if (!vis[v]) { vis[v] = 1; Q[qt++] = v; } } } } void calc() { bfs(come, E); static vector <int> E2[MAXN]; rep (i, n) E2[i].clear(); rep (i, n) rep (j, E[i].size()) E2[E[i][j]].push_back(i); bfs(go, E2); } }SCC; int main() { while (scanf("%d%d%d", &n, &m, &e) != EOF) { static map <int, int> id[MAXN]; rep (i, n) E[i].clear(), id[i].clear(); rep (i, e) { char s[10]; scanf("%s", s); int u = trans(s); int v = trans(s + 3); E[u].push_back(v); assert(!id[u].count(v)); id[u][v] = i; } hopcroft_karp(); SCC.clear(n + m); rep (u, n) { if (match1[u] != -1) SCC.addedge(match1[u] + n, u); rep (i, E[u].size()) { int v = E[u][i]; if (v != match1[u]) SCC.addedge(u, v + n); } } SCC.gao(); rep (i, m) if (match2[i] == n) SCC.go[n + i] = 1; rep (i, n) if (match1[i] == -1) SCC.come[i] = 1; SCC.calc(); vector <int> ans; rep (u, n) rep (i, E[u].size()) { int v = E[u][i]; if (SCC.come[u] || SCC.go[v + n] || SCC.id[u] == SCC.id[v + n] || match1[u] == v) continue; ans.push_back(id[u][v]); } sort(ans.begin(), ans.end()); printf("%d\n", (int)ans.size()); rep (i, ans.size()) { if (i) putchar(' '); printf("%d", ans[i]); } puts(""); } }
其实,其实这题网络流就能过的 = =||
毕竟二分图上dinic和HK是一样复杂度的呀……能过应该比较正常
dinic的复杂度不是n^2*e吗?
dinic跑二分图匹配是sqrt(n) * e的……