题面
曾经有一个国王,他有 N 个儿子。
王国中有着 N 个漂亮的姑娘,每个王子也都有自己喜欢的对象。
每个王子喜欢的对象可能不止一个。
因为王子们都到了结婚的年纪,所以国王想让王子们娶了这 N 个姑娘,当然每个姑娘只能嫁给一名王子。
国王请巫师为他做一个统计,他想看看儿子们都有哪些喜欢的姑娘。
就这样,巫师制作了一个清单,上面具体列出了每一个王子喜欢哪些姑娘,并给出了一套初步的配对方案。
国王看了看巫师给他列出的清单,说道:“你总结的不错,但是我并不完全满意。我希望你列出每个王子可以婚配的女子清单,可以满足每个王子对应的清单上都是他喜欢的姑娘,并且任何一个王子从自己的清单上任意选择一名姑娘作为自己的结婚对象之后,剩下的王子仍然能够从自己的清单中选择的到自己喜欢的对象,使得所有的王子都能完成和自己喜欢的姑娘配对。”
请你帮助巫师列出国王满意的清单。
输入格式
第一行包含整数 N。
接下来 N 行,每行包含多个整数,描述了一个王子喜欢的姑娘的清单,每行的第一个整数 K ,表示王子喜欢的姑娘的数量,接下来的 K 个整数,为这 K 个姑娘的编号。
最后一行,包含 N 个整数,表示初步的配对方案,第 i 个整数表示与第 i 个王子进行配对的姑娘编号。
输出格式
输出共 N 行。
每行包含多个整数,描述一个王子可以婚配的女子清单,第一个整数 L ,表示王子可以婚配的姑娘的数量,接下来 L 个整数,为这 L 个姑娘的编号(请按升序排列)。
数据范围
1≤N≤2000,
所有 K 加起来的和不超过200000。
输入样例:
4
2 1 2
2 1 2
2 2 3
2 3 4
1 2 3 4
输出样例:
2 1 2
2 1 2
1 3
1 4
题解
姑娘根本不喜欢王子,一切都是国王的任务罢了
好家伙. 互抢老婆呗,
王子跟名单中的姑娘结婚不影响最大匹配就行了
巫师给每个王子$a_i$配了个姑娘$b_i$, 现在王子$a_i$想抢走$b_j$
那$a_j$怎么办呢?
1.$a_j$把$b_i$的抢过来呗
2.$a_j$抢$b_{k_1}$, $a_{k_1}$抢$b_{k_2}$ .. 直到$a_{k_j}$抢$b_i$呗
说白了, 让$a_j$抢一个圈出来, 多人(2人)互绿呗
放到二分图无非是, $a_i$ 匹配 $b_j$, 然后 dfs(match[$b_j$]) return 1; 代表 $a_i$可以抢$$b_j
但存在 $a_i$想抢的(喜欢)姑娘太多了, 每个跑一遍匈牙利要超时
其实我们已经发现了, 只要在一个环内的(SCC), ~`就可以互绿~~, 边怎么建呢?
王子和每个喜欢的姑娘建有向边, 原配姑娘和王子建有向边(这条边就是 match[$b_i$])
从这里我们也能看到, 匈牙利算法, 和有向图之间的一些关联
#include <bits/stdc++.h>
using namespace std;
const int N = 4e3 + 5;
int n, m, _, k;
vector<int> h[N];
int dfn[N], low[N], df, st[N], top;
int c[N], scnt;
bool inst[N];
void tarjan(int x) {
dfn[x] = low[x] = ++df; inst[st[++top] = x] = 1;
for (auto &y : h[x])
if (!dfn[y]) tarjan(y), low[x] = min(low[x], low[y]);
else if (inst[y]) low[x] = min(low[x], dfn[y]);
if (low[x] == dfn[x]) {
++scnt; int y;
do inst[y = st[top--]] = 0, c[y] = scnt; while (y != x);
}
}
int main() {
cin >> n;
for (int i = 1; i <= n; ++i) for (cin >> _; _; --_) cin >> m, h[i].emplace_back(m + n);
for (int i = 1; i <= n; ++i) cin >> m, h[m + n].emplace_back(i);
for (int i = 1; i <= n; ++i) if (!dfn[i]) top = 0, tarjan(i);
for (int i = 1; i <= n; ++i) {
vector<int> ans;
for (auto &y : h[i]) if (c[i] == c[y]) ans.emplace_back(y - n);
sort(ans.begin(), ans.end()); cout << ans.size();
for (auto &j : ans) cout << ' ' << j; cout << '\n';
}
return 0;
}