ContestHunter#17-C 舞动的夜晚
Description:
L公司和H公司举办了一次联谊晚会。晚会上,L公司的N位员工和H公司的M位员工打算进行一场交际舞。在这些领导中,一些L公司的员工和H公司的员工之间是互相认识的,这样的认识关系一共有T对。舞会上,每位员工会尝试选择一名Ta认识的对方公司的员工作为舞 伴,并且每位员工至多跳一支舞。完成的交际舞的数量越多,晚会的气氛就越热烈。顾及到晚会的气氛,员工们希望知道,哪些员工之间如果进行了交际舞,就会使整场晚会能够完成的交际舞的最大数量减小。
Input
第一行三个整数N、M、T。 接下来T行每行两个整数x、y,表示L公司的员工x和H公司的员工y互相认识。
output
第一行一个整数cnt,表示进行了交际舞后会使整场晚会能够完成的交际舞的最大数量减小的员工有多少对。 第二行cnt个整数,升序输出这样的一对员工的认识关系的编号(他们的认识关系是在输入数据中读入的第几条认识关系)。如果cnt=0,输出一个空行。
思路:判断二分图匹配的可行变与不可行边,就是二分图跑完网络流后,将匹配边所对应的两点加入新的图,然后跑一个tarjan
此时必须边的判定条件为:(x,y)流量为1,并且在残量网络上属于不同的强联通分量。可行边的判断条件为(x,y)的流量为1,或者在残量网络上属于同一个前强联通分量
#include<iostream> #include<cstring> #include<vector> #include<queue> #include<stack> using namespace std; const int N = 200200, M = 1000500, INF = 1e9; int head[N], now = 1; struct edges{ int to,next,w; }edge[M<<1]; void add(int u,int v,int w){ edge[++now].to = v, edge[now].next = head[u], edge[now].w = w; head[u] = now; } int n, m, q, s, t, maxflow, dep[N]; struct input{ int x,y; }inp[M]; bool bfs(){ memset(dep,0,sizeof(dep)); queue<int> q; q.push(s); dep[s] = 1; while(!q.empty()){ int x = q.front(); q.pop(); for(int i = head[x]; i; i = edge[i].next){ int v = edge[i].to; if(edge[i].w && !dep[v]){ q.push(v); dep[v] = dep[x] + 1; if(v == t) return 1; } } } return 0; } int dinic(int x,int flow){ if(x == t) return flow; int rest = flow, k; for(int i = head[x]; i && rest; i = edge[i].next){ int v = edge[i].to; if(edge[i].w && dep[v] == dep[x] + 1){ k = dinic(v, min(rest, edge[i].w)); if(!k) dep[v] = 0; edge[i].w -= k; edge[i ^ 1].w += k; rest -= k; } } return flow - rest; } vector<int> vec[N], ans; void add2(int u,int v) { vec[u].push_back(v);} int dfn[N], low[N], cnt, tot, dict[N]; bool vis[N]; stack<int> sta; void tarjan(int x){ dfn[x] = low[x] = ++cnt; sta.push(x); vis[x] = 1; for(int i = 0; i < vec[x].size(); i++){ int v = vec[x][i]; if(!dfn[v]){ tarjan(v); low[x] = min(low[x], low[v]); } else if(vis[v]) low[x] = min(low[x], dfn[v]); } if(dfn[x] == low[x]){ tot++; int temp = -1; do{ temp = sta.top(); sta.pop(); vis[temp] = 0; dict[temp] = tot; }while(temp != x); } return ; } int main(){ ios::sync_with_stdio(false); cin>>n>>m>>q; int x,y; for(int i = 1; i <= q; i++){ //左部节点为1 ~ n,右部节点为 n ~ n + m cin>>x>>y; y += n; inp[i].x = x, inp[i].y = y; add(x,y,1); add(y,x,0); } s = 0, t = n+m+1; for(int i = 1; i <= n; i++) add(s,i,1), add(i,s,0); for(int i = n+1; i <= n+m; i++) add(i,t,1), add(t,i,0); int tmp = 0; while(bfs()) while(tmp = dinic(s,INF)) maxflow += tmp; //最大流解二分图匹配 for(int i = 2; i <= now; i++) //将匹配了的边所对应的两点加入新图 if(edge[i].w) add2(edge[i ^ 1].to, edge[i].to); for(int i = s; i <= t; i++) //tarjan找scc if(!dfn[i]) tarjan(i); for(int i = 1; i <= q; i++) //关于不可行边的判断条件 if(dict[inp[i].x] != dict[inp[i].y] && edge[i * 2].w != 0) ans.push_back(i); int tot = ans.size(); cout<<tot<<endl; for(int i = 0; i < ans.size(); i++){ if(i < tot - 1) cout<<ans[i]<<" "; else cout<<ans[i]<<endl; } return 0; }
注意最后空格的输出……不然PE