大致题意:
有n个帅哥要泡n个美女。对于每个帅哥,给出他可以选择的美女序号。然后给出一个可行的匹配。对于每个帅哥,求出他可以选择哪些美女,才能使得所有帅哥都有马子泡。
大致思路:
很神奇的一道强连通题目,点数达到8000,边数达到20000000,肯定不能够直接爆搜。更诡异的是他给出了一组可行的匹配,这和题目有关系么?其实,这个才是解题的关键!把所有的帅哥和美女都抽象成有向图的节点,如果一个帅哥i可以选择美女j,则连接i->j。如果在给出的可行匹配中如果帅哥i匹配美女j,则连边j->i。
对这个图求出强联通分量,可以看出在一个强连通分量中帅哥的个数等于美女。对于帅哥i,i所在的强连通分量中所有的他喜欢的美女他都可以选择,而不用顾忌影响其他帅哥。所以输出他所在的强连通分量中他喜欢的美女编号即可。
另外不知道为什么最后判断男女是否处于同一分量时必须使用原始的邻接表, 而不能直接在下标[n, 2n)中判断~~囧
#include<iostream> #include<cstdio> #include <algorithm> #include<cstring> using namespace std; const int inf=1<<30; const int nMax=30015; const int mMax=500100; class edge{ public: int v,nex; };edge e[mMax]; int k,head[nMax];//head[i]是以点i为起点的链表头部 void addedge(int a,int b){//向图中加边的算法,注意加上的是有向边//b为a的后续节点既是a---->b e[k].v=b; e[k].nex=head[a]; head[a]=k;k++; } int dfn[nMax],low[nMax],sta[nMax],top,atype,belon[nMax],dep; //atype 强连通分量的个数 bool insta[nMax]; void Tarjan(int u){ //我的Tarjan模版 int i,j; dfn[u]=low[u]=++dep; sta[++top]=u; insta[u]=1; for(i=head[u];i;i=e[i].nex){ int v=e[i].v; if(!dfn[v]){ Tarjan(v); low[u]=min(low[u],low[v]); } else{ if(insta[v])low[u]=min(low[u],dfn[v]); } } if(dfn[u]==low[u]){ atype++; //强连通分量个数 do{ j=sta[top--]; belon[j]=atype; //第j个点属于第type个连通块 insta[j]=0; }while(u!=j); } } void init(){ k=1; dep=1; top=atype=0; memset(insta,0,sizeof(insta)); //是否在栈中 memset(head,0,sizeof(head)); //静态链表头指针 memset(low,0,sizeof(low)); //Tarjan的low数组 memset(dfn,0,sizeof(dfn)); //Tarjan的dfn数组 memset(belon,0,sizeof(belon)); //记录每个点属于哪一个强连通分量 } int ans[nMax]; int main(){ int n,a,m,i,j,b; while(scanf("%d",&n)!=EOF){ init(); for(i=1;i<=n;i++){ scanf("%d",&m); while(m--){ scanf("%d",&a); addedge(i,n+a); } } for(i=1;i<=n;i++){ scanf("%d",&a); addedge(n+a,i); } for(i=1;i<=2*n;i++){ if(!dfn[i]){ Tarjan(i); } } for(i=1;i<=n;i++){ b=0; for(j=head[i];j;j=e[j].nex){ int v=e[j].v; if(belon[i]==belon[v]){ ans[b++]=v-n; } } sort(ans,ans+b); printf("%d ",b); for(j=0;j<b;j++){ printf("%d ",ans[j]); } printf("\n"); } } return 0; }