King's Quest
Time Limit: 15000MS | Memory Limit: 65536K | |
Total Submissions: 10352 | Accepted: 3815 |
题目链接:http://poj.org/problem?id=1904
Description:
Once upon a time there lived a king and he had N sons. And there were N beautiful girls in the kingdom and the king knew about each of his sons which of those girls he did like. The sons of the king were young and light-headed, so it was possible for one son to like several girls.
So the king asked his wizard to find for each of his sons the girl he liked, so that he could marry her. And the king's wizard did it -- for each son the girl that he could marry was chosen, so that he liked this girl and, of course, each beautiful girl had to marry only one of the king's sons.
However, the king looked at the list and said: "I like the list you have made, but I am not completely satisfied. For each son I would like to know all the girls that he can marry. Of course, after he marries any of those girls, for each other son you must still be able to choose the girl he likes to marry."
The problem the king wanted the wizard to solve had become too hard for him. You must save wizard's head by solving this problem.
Input:
The first line of the input contains N -- the number of king's sons (1 <= N <= 2000). Next N lines for each of king's sons contain the list of the girls he likes: first Ki -- the number of those girls, and then Ki different integer numbers, ranging from 1 to N denoting the girls. The sum of all Ki does not exceed 200000.
The last line of the case contains the original list the wizard had made -- N different integer numbers: for each son the number of the girl he would marry in compliance with this list. It is guaranteed that the list is correct, that is, each son likes the girl he must marry according to this list.
Output:
Output N lines.For each king's son first print Li -- the number of different girls he likes and can marry so that after his marriage it is possible to marry each of the other king's sons. After that print Li different integer numbers denoting those girls, in ascending order.
Sample Input:
4 2 1 2 2 1 2 2 2 3 2 3 4 1 2 3 4
Sample Output:
2 1 2 2 1 2 1 3 1 4
题意:
有n个王子n个公主,每个王子都有其喜欢的公主,每个公主都可以接受所有王子(= =),然后起初会给你一个婚姻方案。现在让你输出每个王子所有可以结婚的对象,并且他们结婚后,不会影响其它王子的婚姻。
题解:
这还是一个挺有意思的题。因为这求的是方案数,所以二分图匹配在这里不太适用。
考虑一下一个集合,这个集合中的所有王子都喜欢这个集合中的所有公主,并且满足题中条件,那么也就是说,所有王子都是可达某个公主的。
我们来考虑一下用连通性求解,对于原先给出的匹配,我们由公主连向王子,对于所有王子喜欢的公主,就由王子连向公主。然后利用tarjan求强连通分量。对于每一个强连通分量集合中的点,都是满足条件的。因为集合中的元素都是互相可达的,王子选择了一个,不会影响另一个人的婚姻。至于这里公主向王子的连边,可以理解为“撤销操作”:当一个公主被一个王子选取过后,那么原来选择这个公主的王子就可以撤销这个操作选择其它公主。如果存在强连通分量,那么这个撤销操作是肯定可以到达另外一个公主的,当然也可能不到达,就在男生这里终止。
还需要注意的一点就是,可能集合中的某对王子、公主,可能王子本身就不喜欢那个公主,也就是本来可以的撤销操作在王子那里就终止了。这时要注意一下输出。
代码如下:
#include <cstdio> #include <cstring> #include <algorithm> #include <iostream> #include <stack> #include <vector> using namespace std ; typedef long long ll; const int N = 4005; int n,tot; int head[N]; struct Edge{ int u,v,next; }e[4100010]; void adde(int u,int v){ e[tot].v=v;e[tot].next=head[u];head[u]=tot++; } vector <int> ans[N],g[N]; stack <int> s; int T,num; int scc[N],dfn[N],low[N],vis[N]; void Tarjan(int u){ dfn[u]=low[u]=++T;vis[u]=1; s.push(u); for(int i=head[u];i!=-1;i=e[i].next){ int v=e[i].v; if(!vis[v]){ Tarjan(v); low[u]=min(low[u],low[v]); }else if(!scc[v]){ low[u]=min(low[u],dfn[v]); } } if(low[u]==dfn[u]){ num++;int now; do{ now = s.top();s.pop(); scc[now]=num; }while(!s.empty() && now!=u); } } int main(){ while(scanf("%d",&n)!=EOF){ memset(scc,0,sizeof(scc)); memset(dfn,0,sizeof(dfn)); memset(vis,0,sizeof(vis)); memset(head,-1,sizeof(head)); for(int i=1;i<=n;i++) g[i].clear(),ans[i].clear(); num=0;T=0;tot=0; for(int i=1;i<=n;i++){ int k; scanf("%d",&k); for(int j=1;j<=k;j++){ int l; scanf("%d",&l); g[i].push_back(l); adde(i,l+n); } } for(int i=1;i<=n;i++){ int l; scanf("%d",&l); adde(l+n,i); } for(int i=1;i<=2*n;i++){ if(!vis[i]) Tarjan(i); } for(int i=1;i<=n;i++){ for(int j=head[i];j!=-1;j=e[j].next){ int v=e[j].v;v-=n; if(scc[i]==scc[v+n]) ans[i].push_back(v); } } for(int i=1;i<=n;i++){ printf("%d ",(int)ans[i].size()); sort(ans[i].begin(),ans[i].end()); for(int j=0;j<ans[i].size();j++){ printf("%d ",ans[i][j]); } printf(" "); } } return 0; }