LOJ2276 [HAOI2017] 新型城市化 【二分图匹配】【tarjan】
题目分析:
这题出的好!
首先问题肯定是二分图的最大独立集,如果删去某条匹配边之后独立集是否会变大。
跑出最大流之后流满的边就是匹配边。
如果一个匹配边的两个端点在一个强连通分量里,那这条边删掉之后我们就可以找到一个替代方案使得匹配不变小。
具体的,假设这两个点是x,y。因为两者之间连的是匹配边,那么存在一个路径从t->y->x->s。那只要从s有另一条路径到y或者从x有另一条路径到t那就构成一个强连通分量,我们只考虑s到y的情况。
如果存在一条这样的路,我们会发现每次从X集合跳到Y集合的时候走的是黑边,从Y集合跳到X集合的时候走的是红遍,因为我们的目标节点在Y集合,所以采用匈牙利树的分析方法,黑边总比红边多1,所以可以把这条路径翻转达到我们的目的。
代码:
1 #include<bits/stdc++.h> 2 using namespace std; 3 4 const int maxm = 400000,maxn = 20300; 5 6 struct edge{int from,to,flow;}edges[maxm]; 7 int num,n,m,arr[maxn]; 8 vector <int> g[maxn]; 9 int color[maxn]; 10 11 namespace BioGraph{ 12 vector<int> T[maxn]; 13 queue<int> Q; 14 void pd(){ 15 for(int i=1;i<=n;i++){ 16 if(arr[i]) continue; 17 Q.push(i); arr[i] = 1; color[i] = 0; 18 while(!Q.empty()){ 19 int k = Q.front(); Q.pop(); 20 for(int i=0;i<T[k].size();i++){ 21 int z = T[k][i]; 22 if(arr[z]) continue; 23 Q.push(z); color[z] = (color[k]^1); arr[z] = 1; 24 } 25 } 26 } 27 } 28 } 29 namespace SCC{ 30 int dfn[maxn],low[maxn],scc[maxn],sccnum,cl; 31 stack<int> sta; 32 void Tarjan(int now){ 33 low[now] = dfn[now] = ++cl; 34 sta.push(now); 35 for(int i=0;i<g[now].size();i++){ 36 if(edges[g[now][i]].flow == 0) continue; 37 int to = edges[g[now][i]].to; 38 if(arr[to]) continue; 39 if(dfn[to]) low[now] = min(low[now],dfn[to]); 40 else{Tarjan(to);low[now] = min(low[now],low[to]);} 41 } 42 if(low[now]==dfn[now]){ 43 sccnum++; 44 while(true){ 45 int pi = sta.top(); sta.pop(); 46 arr[pi] = 1; scc[pi] = sccnum; 47 if(now == pi) break; 48 } 49 } 50 } 51 } 52 53 void AddEdge(int x,int y,int v){ 54 edges[num++] = (edge){x,y,v}; 55 edges[num++] = (edge){y,x,0}; 56 g[x].push_back(num-2); 57 g[y].push_back(num-1); 58 } 59 60 int dis[maxn],cur[maxn]; 61 queue<int> qq; 62 int BFS(){ 63 qq.push(0); memset(dis,-1,sizeof(dis)); dis[0] = 1; 64 while(!qq.empty()){ 65 int k = qq.front(); qq.pop(); 66 for(int i=0;i<g[k].size();i++){ 67 edge sm = edges[g[k][i]]; 68 if(sm.flow && dis[sm.to]== -1){ 69 dis[sm.to] = dis[k]+1; 70 qq.push(sm.to); 71 } 72 } 73 } 74 return dis[n+1]; 75 } 76 77 int dfs(int x,int a){ 78 if(x == n+1 || a == 0) return a; 79 int flow = 0,f; 80 for(int &i=cur[x];i<g[x].size();i++){ 81 edge &xx = edges[g[x][i]]; 82 if(dis[xx.to]>dis[x]&&(f=dfs(xx.to,min(a,xx.flow)))){ 83 xx.flow -= f; 84 flow += f; 85 a -= f; 86 edges[g[x][i]^1].flow+=f; 87 if(a == 0) return flow; 88 } 89 } 90 return flow; 91 } 92 93 void read(){ 94 scanf("%d%d",&n,&m); 95 for(int i=1;i<=m;i++){ 96 int x,y; scanf("%d%d",&x,&y); 97 BioGraph::T[x].push_back(y); 98 BioGraph::T[y].push_back(x); 99 } 100 BioGraph::pd(); 101 for(int i=1;i<=n;i++){ 102 if(color[i]) {AddEdge(i,n+1,1);continue;} 103 else AddEdge(0,i,1); 104 for(int j=0;j<BioGraph::T[i].size();j++){ 105 int z = BioGraph::T[i][j]; 106 AddEdge(i,z,1); 107 } 108 } 109 } 110 111 vector<pair<int,int> > res; 112 void work(){ 113 int flow = 0; 114 while(BFS() != -1){ 115 memset(cur,0,sizeof(cur)); 116 flow += dfs(0,19260817); 117 } 118 memset(arr,0,sizeof(arr)); 119 for(int i=0;i<=n+1;i++){ 120 if(arr[i]) continue; 121 SCC::Tarjan(i); 122 } 123 for(int i=0;i<num;i++){ 124 if(edges[i].from == 0 || edges[i].to == n+1) continue; 125 if(edges[i].from == n+1 || edges[i].to == 0) continue; 126 if(color[edges[i].from]) continue; 127 if(edges[i].flow) continue; 128 if(SCC::scc[edges[i].from] == SCC::scc[edges[i].to]) continue; 129 int x=min(edges[i].from,edges[i].to),y=max(edges[i].from,edges[i].to); 130 res.push_back(make_pair(x,y)); 131 } 132 sort(res.begin(),res.end()); 133 int z = res.size(); printf("%d\n",z); 134 for(int i=0;i<z;i++){ printf("%d %d\n",res[i].first,res[i].second); } 135 } 136 137 int main(){ 138 read(); 139 work(); 140 return 0; 141 }