/* 题意:给你一个二分图,给定初始的完全匹配,判断更改某个X集合的匹配对象,时候还存在完全匹配 题解:强联通分量,对于非初始匹配的边,建由X到Y的单向边,反正建由Y到X的单向边,由增广路原理可知道,非初始匹配边只要在同一个强联通分量里面,必然可以重构一个完全匹配 */ #include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #include<climits> #include<vector> using namespace std; const int MAXV=4009; int n; struct Person { int n;//X集合的出边出度 int q[2001];//与i向量的Y中元素 }p[2001]; struct Edge { int v,next; }edge[MAXV*MAXV]; int first[MAXV],Link[MAXV],ins[MAXV],dfn[MAXV],low[MAXV],Stack[MAXV],scc[MAXV],ans[MAXV]; int e,top,index,num; void add(int u,int v) { edge[e].v=v; edge[e].next=first[u]; first[u]=e++; } void tarjan(int u) { int v; low[u] = dfn[u] = index++; Stack[++top]=u; ins[u] = true; // 枚举每一条边:u-->v for (int k=first[u]; k!=-1; k=edge[k].next) { v = edge[k].v; if (dfn[v] == 0) { tarjan(v); low[u]=min(low[u],low[v]); } else if (ins[v]) { low[u]=min(low[u],dfn[v]); } } // 如果节点u是强连通分量的根 if (dfn[u] == low[u]) { num++; do { v = Stack[top--]; ins[v] = false; scc[v] = num; }while (u != v); } } void solve() { for(int i=1;i<=n;i++) { for(int j=0;j<p[i].n;j++) { if(Link[i]!=p[i].q[j]+n) { add(i,p[i].q[j]+n); } else { add(p[i].q[j]+n,i); } } } memset(dfn,0,sizeof(dfn)); memset(low,0,sizeof(low)); memset(ins,0,sizeof(ins)); memset(scc,0,sizeof(scc)); index=1; top=-1; num=0; for(int i=1;i<=2*n;i++) { if(dfn[i]==0) { //cout<<i<<endl; tarjan(i); } } int key=0; for(int i=1;i<=n;i++) { key=0; for(int j=0;j<p[i].n;j++) if(scc[p[i].q[j]+n]==scc[i]) { ans[key++]=p[i].q[j]; } if(key==0) { ans[key++]=Link[i]-n; } sort(ans,ans+key); printf("%d",key); for(int i=0;i<key;i++) { printf(" %d",ans[i]); } printf("\n"); } } int main() { int tmp,k; while(scanf("%d",&n)!=EOF) { memset(first,-1,sizeof(first)); memset(Link,-1,sizeof(Link)); e=0; for(int i=1;i<=n;i++) { scanf("%d",&p[i].n); for(int j=0;j<p[i].n;j++) { scanf("%d",&p[i].q[j]); } } for(int i=1;i<=n;i++) { scanf("%d",&tmp); Link[i]=tmp+n;//X集合中i的初始匹配 } solve(); } return 0; }