hdu4685Prince and Princess 二分圖+Tarjan縮點
阿新 • • 發佈:2018-11-06
hdu4685Prince and Princess
題目傳送門
分析
題目大意:一個王子喜歡好多妹子,現在要讓最多的王子妹子雙宿雙飛,同時求出一個王子可以和多少個妹子結婚並且不影響別的王子妹子雙宿雙飛的對數。
很強的一道二分圖,是poj1904的加強版本。
有一個正經的名字叫二分圖匹配的可行邊。
首先考慮完備匹配的情況。
一種很神仙的做法是,王子照樣向妹子連邊,但是如果某個妹子匹配了王子,把那個妹子向王子連邊,然後跑Tarjan,在一個強連通分量裡的王子可以任意匹配妹子。
因為如果幾個王子和妹子在同一個強連通分量裡,由於一個妹子只會連回去一條邊,所以說如果一個王子想要上
然後如果去搞別連通塊的妹子肯定會橫刀奪愛。
再考慮單身的情況。
咳咳,假設匹配數為 ,那麼單身的 個公主造 個王子匹配,單身的 個王子造 個公主匹配。
新造的王子要喜歡
然後匹配一波就是完備匹配啦。
程式碼
碼農題,Tarjan縮點忘光光了。
再次鄙視多組資料
#include<bits/stdc++.h>
#define mem(x) memset(x, 0, sizeof(x))
const int N = 2e3 + 10, M = N * N;
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 pr[N], las[N], dfn[N], low[N], be[N], st[N], nx[M], vis[N], to[M], tm, tot, tp, cnt; bool mark[N], g[N][N];
void add(int u, int v) {to[++tp] = v; nx[tp] = pr[u]; pr[u] = tp;}
bool dfs(int u, int m) {
for(int i = 1;i <= m; ++i)
if(vis[i] != tm && g[u][i]) {
vis[i] = tm;
if(!las[i] || dfs(las[i], m))
return las[i] = u, true;
}
return false;
}
int Hungary(int n, int m) {
int r = 0;
for(int i = 1;i <= m; ++i) las[i] = 0;
for(int i = 1;i <= n; ++i) ++tm, r += dfs(i, m);
return r;
}
void Tarjan(int u) {
dfn[u] = low[u] = ++tot; st[++tp] = u; mark[u] = true;
for(int i = pr[u]; i; i = nx[i])
if(!dfn[to[i]]) Tarjan(to[i]), low[u] = std::min(low[u], low[to[i]]);
else if(mark[to[i]]) low[u] = std::min(low[u], dfn[to[i]]);
if(dfn[u] == low[u])
for(++cnt;st[tp + 1] != u; mark[st[tp--]] = false)
be[st[tp]] = cnt;
}
void Clr() {
cnt = tp = 0;
mem(pr); mem(g); mem(st); mem(dfn); mem(low); mem(mark);
}
int main() {
for(int C = 1, T = ri(); C <= T; ++C) {
Clr(); int n = ri(), m = ri();
for(int i = 1;i <= n; ++i)
for(int x = ri();x--;) g[i][ri()] = true;
int r = Hungary(n, m);
int nn = n + m - r;
for(int i = n + 1;i <= nn; ++i)
for(int j = 1;j <= nn; ++j)
g[i][j] = true;
for(int i = 1;i <= n; ++i)
for(int j = m + 1;j <= nn; ++j)
g[i][j] = true;
Hungary(nn, nn);
for(int i = 1;i <= nn; ++i)
if(las[i]) add(i + nn, las[i]);
for(int i = 1;i <= nn; ++i)
for(int j = 1; j <= nn; ++j)
if(g[i][j]) add(i, j + nn);
for(int i = 1;i <= nn << 1; ++i) if(!dfn[i]) tp = tot = 0,Tarjan(i);
printf("Case #%d:\n", C);
for(int i = 1;i <= n; ++i) {
tp = 0;
for(int j = 1;j <= m; ++j) if(g[i][j] && be[j + nn] == be[i]) st[++tp] = j;
printf("%d", tp);
for(int j = 1;j <= tp; ++j) printf(" %d", st[j]);
puts("");
}
}
return 0;
}