题意:有N个王子和N个妹子;(1 <= N <= 2000)第i个王子喜欢Ki个妹子;(详见sample)题给一个完美匹配,即每一个王子和喜欢的一个妹子结婚;问每一个王子可以有几种选择(在自己喜欢的妹子里面选),并输出可选的妹子的标号(升序);
Sample Input
4 (N) 2 1 2 (Ki) 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
分析:图匹配问题,1~N为王子的编号,N~2N为妹子的编号;输入有向边;
重点: 对于给定的一组匹配,看做是反向边;即从妹子指回到王子;这样进行Tarjan缩点之后,就可以遍历边(要在王子喜欢的妹子的选...)看是否还在同一个强连通分量中,若妹子还是和王子在同一个scc中,即可婚配;
证明:为什么说还在一个强连通分量中就可以?边一定是连接王子和妹子的,在不重复走一条边的前提下,会知道王子和妹子的个数是相同的;并且每条边都符合王子喜欢妹子的条件;
ps:该题第一次使用了输出外挂,很好用啊!!时间之间减了至少1/10...
思维坑点:认为可以直接在Tarjan缩点时,就把每个强连通分量里面的妹子写入vec[]中;这样之后就可以直接对每个vec排序之后,之后调用belong[]输出所在的scc的个数即妹子的编号。。想法是好的,但是题意啊!!!并不是在一个连通分量的妹子都是这样王子喜欢的。。。所以要遍历边,找到在一个连通分量里面的;
// 532ms #include<iostream> #include<cstdio> #include<cstring> #include<string.h> #include<algorithm> #include<map> #include<queue> #include<vector> #include<cmath> #include<stdlib.h> #include<time.h> #include<stack> #include<set> using namespace std; #define rep0(i,l,r) for(int i = (l);i < (r);i++) #define rep1(i,l,r) for(int i = (l);i <= (r);i++) #define rep_0(i,r,l) for(int i = (r);i > (l);i--) #define rep_1(i,r,l) for(int i = (r);i >= (l);i--) #define MS0(a) memset(a,0,sizeof(a)) #define MS1(a) memset(a,-1,sizeof(a)) #define pb push_back template<typename T> void read(T &m) { T x=0,f=1;char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();} m = x*f; } template<typename T> void out(T a) { if(a>9) out(a/10); putchar(a%10+'0'); } const int N = 2020<<1;//倍增点数 const int M = 202200; int head[M],tot; struct edge{ int to,w,Next; }e[M]; void ins(int a,int b,int w = 0) { e[++tot].Next = head[a]; e[tot].to = b; e[tot].w = w; head[a] = tot; } int pre[N],dfs_clock,low[N]; int belong[N],scc,n; stack<int> S; bool stk[N]; void Tarjan(int u) { pre[u] = low[u] = ++dfs_clock; S.push(u); stk[u] = true; int v;//点u所在连通分量的出度; for(int i = head[u];i;i = e[i].Next){ v = e[i].to; if(pre[v] == 0){ Tarjan(v); low[u] = min(low[u],low[v]); }else if(stk[v]){ low[u] = min(low[u],pre[v]); } } if(pre[u] == low[u]){//强连通分量的根节点 ++scc; do{ v = S.top(); S.pop();stk[v] = false; //if(v <= n) belong[v] = scc; //else vec[scc].pb(v); }while(v != u); } } int ans[N]; int main() { int v,T,kase = 1; read(n); rep1(u,1,n){ int k; read(k); rep0(j,0,k){ read(v); ins(u,v+n);//妹子标号要加上n; } } rep1(u,1,n){ read(v); ins(v+n,u);//反向边*** } rep1(u,1,n)if(pre[u] == 0) Tarjan(u); rep1(u,1,n){ int cnt = 0; for(int i = head[u];i;i = e[i].Next){//遍历边 v=e[i].to; if(belong[u] == belong[v]) //同一个强连通分量 ans[cnt++] = v-n; } sort(ans,ans+cnt); out(cnt); rep0(i,0,cnt){ putchar(' '); out(ans[i]); } puts(""); } return 0; }