题目链接(洛谷):https://www.luogu.org/problemnew/show/P3731

题意概述:给出一张二分图,询问删掉哪些边之后可以使这张二分图的最大独立集变大。N<=10000,0<=M<=min (150000,N(N-1)/2).

这个题首先你得总结出题意就是这个样子不然就是凉的。。。。。

二分图的最大独立集,可以想到网络流完成(定理:二分图的最大独立集=二分图点数-二分图最大匹配)。当最小割边小的时候独立集就变大了,因此问题变删掉哪些边可以让最小割变小。

这个问题就有经典的做法:先求出最小割,然后在残量网络上面跑tarjan强连通缩点,考察所有的满流的边,如果一条边满流并且两个端点不在同一个强连通分量中,那么这条边就存在与某个最小割中,删掉之后就相当于没有花代价把这条边割掉了,使得最小割变小(最大流变大)。

简单证明:

必要性:首先某一条边如果不满流,那么一定不是最小割中的边;如果一条满流边的反向边(它包含在残量网络中)在某个强连通分量中则可以视作给这条边找到了一个退流的方法,使得这条边(原图中)的起点处的流量可以绕过这条边到达这条边的终点。由于最小割是割掉之后源点汇点无法流通,这条边也显然不在最小割中。

充分性:当一条边满流并且反向边不在一个残量网络的强连通分量中的时候,这条边可能在最小割中,又由于这条边的反向边不在强连通分量中,我们没有办法找到一种方案使得这条边上的流量退去之后源点到汇点的流量不变。因此这条边一定在某个最小割之中,当这条边割掉之后,就相当于采用了这个最小割的方案,最小割变小。

注意一下一开始的建图,现在无向图上跑二分图确定好集合,如果用网络流跑二分图一定要注意连边必须是有向边,不然你会发现无向二分图跑最小流根本就不是匹配的意义。。。

  1. #include<iostream>
  2. #include<cstdio>
  3. #include<cstring>
  4. #include<cstdlib>
  5. #include<algorithm>
  6. #include<cmath>
  7. #include<queue>
  8. #include<set>
  9. #include<map>
  10. #include<vector>
  11. #include<cctype>
  12. using namespace std;
  13. const int MAXN=;
  14. int N,M;
  15. struct data{
  16. int a,b;
  17. friend bool operator < (data x,data y){
  18. return x.a<y.a||x.a==y.a&&x.b<y.b;
  19. }
  20. }D[MAXN]; int ans;
  21. struct graph{
  22. static const int maxn=;
  23. static const int maxm=;
  24. struct edge{ int to,next; }E[maxm<<];
  25. int first[maxn],np,color[maxn];
  26. graph(){ np=; }
  27. void add_edge(int u,int v){
  28. E[++np]=(edge){v,first[u]};
  29. first[u]=np;
  30. }
  31. void DFS(int i,int c){
  32. color[i]=-c;
  33. for(int p=first[i];p;p=E[p].next){
  34. int j=E[p].to;
  35. if(color[j]) continue;
  36. DFS(j,color[i]);
  37. }
  38. }
  39. }gp;
  40. struct NET{
  41. static const int maxn=;
  42. static const int maxm=;
  43. static const int inf=1e6;
  44. struct edge{ int from,to,next,cap,flow; }E[(maxm+maxn*)<<];
  45. int S,T,tot,first[maxn],np,cur[maxn],fl[maxn],d[maxn],gap[maxn];
  46. int dfn[maxn],low[maxn],dfs_clock,sccno[maxn],scc_cnt,stk[maxn],top;
  47. NET(){ np=dfs_clock=scc_cnt=; }
  48. void add_edge(int u,int v,int c){
  49. E[++np]=(edge){u,v,first[u],c,};
  50. first[u]=np;
  51. E[++np]=(edge){v,u,first[v],,};
  52. first[v]=np;
  53. }
  54. void BFS(){
  55. queue<int>q;
  56. for(int i=;i<=tot;i++) d[i]=tot;
  57. d[T]=; q.push(T);
  58. while(!q.empty()){
  59. int i=q.front(); q.pop();
  60. for(int p=first[i];p;p=E[p].next){
  61. int j=E[p].to;
  62. if(d[j]==tot) d[j]=d[i]+,q.push(j);
  63. }
  64. }
  65. }
  66. int augment(){
  67. int now=T,flow=inf;
  68. while(now!=S){
  69. flow=min(flow,E[fl[now]].cap-E[fl[now]].flow);
  70. now=E[fl[now]].from;
  71. }
  72. now=T;
  73. while(now!=S){
  74. E[fl[now]].flow+=flow,E[(fl[now]-^)+].flow-=flow;
  75. now=E[fl[now]].from;
  76. }
  77. return flow;
  78. }
  79. int ISAP(){
  80. memcpy(cur,first,sizeof(first));
  81. BFS();
  82. for(int i=;i<=tot;i++) gap[d[i]]++;
  83. int now=S,flow=;
  84. while(d[S]<tot){
  85. if(now==T) flow+=augment(),now=S;
  86. bool ok=;
  87. for(int p=cur[now];p;p=E[p].next){
  88. int j=E[p].to;
  89. if(E[p].cap>E[p].flow&&d[j]+==d[now]){
  90. ok=,cur[now]=fl[j]=p,now=j;
  91. break;
  92. }
  93. }
  94. if(!ok){
  95. int minl=tot;
  96. for(int p=first[now];p;p=E[p].next){
  97. int j=E[p].to;
  98. if(E[p].cap>E[p].flow&&d[j]+<minl) minl=d[j]+;
  99. }
  100. if(--gap[d[now]]==) break;
  101. gap[d[now]=minl]++;
  102. cur[now]=first[now];
  103. if(now!=S) now=E[fl[now]].from;
  104. }
  105. }
  106. return flow;
  107. }
  108. void tarjan_scc(int i){
  109. dfn[i]=low[i]=++dfs_clock;
  110. stk[++top]=i;
  111. for(int p=first[i];p;p=E[p].next){
  112. if(E[p].cap==E[p].flow) continue;
  113. int j=E[p].to;
  114. if(dfn[j]){
  115. if(!sccno[j]) low[i]=min(low[i],dfn[j]);
  116. continue;
  117. }
  118. tarjan_scc(j);
  119. low[i]=min(low[i],low[j]);
  120. }
  121. if(low[i]==dfn[i]){
  122. scc_cnt++;
  123. while(stk[top]!=i) sccno[stk[top--]]=scc_cnt;
  124. sccno[stk[top--]]=scc_cnt;
  125. }
  126. }
  127. }net;
  128. void data_in()
  129. {
  130. scanf("%d%d",&N,&M);
  131. int x,y;
  132. net.S=N+,net.T=N+,net.tot=N+;
  133. for(int i=;i<=M;i++){
  134. scanf("%d%d",&x,&y);
  135. gp.add_edge(x,y); gp.add_edge(y,x);
  136. }
  137. for(int i=;i<=N;i++)
  138. if(!gp.color[i]) gp.DFS(i,);
  139. for(int i=;i<=M*;i+=){
  140. x=gp.E[i].to,y=gp.E[i+].to;
  141. if(gp.color[x]==) net.add_edge(x,y,);
  142. else net.add_edge(y,x,);
  143. }
  144. for(int i=;i<=N;i++){
  145. if(gp.color[i]==) net.add_edge(net.S,i,);
  146. else net.add_edge(i,net.T,);
  147. }
  148. }
  149. void work()
  150. {
  151. net.ISAP();
  152. for(int i=;i<=net.tot;i++)
  153. if(!net.dfn[i]) net.tarjan_scc(i);
  154. for(int i=;i<=*M;i++){
  155. if(net.E[i].cap>net.E[i].flow||net.E[i].flow==) continue;
  156. int x=net.E[i].from,y=net.E[i].to;
  157. if(net.sccno[x]!=net.sccno[y])
  158. D[++ans]=(data){min(x,y),max(x,y)};
  159. }
  160. sort(D+,D+ans+);
  161. printf("%d\n",ans);
  162. for(int i=;i<=ans;i++)
  163. printf("%d %d\n",D[i].a,D[i].b);
  164. }
  165. int main()
  166. {
  167. data_in();
  168. work();
  169. return ;
  170. }

HAOI2017 新型城市化 二分图的最大独立集+最大流+强连通缩点的更多相关文章

  1. 【Luogu3731】[HAOI2017]新型城市化(网络流,Tarjan)

    [Luogu3731][HAOI2017]新型城市化(网络流,Tarjan) 题面 洛谷 给定一张反图,保证原图能分成不超过两个团,问有多少种加上一条边的方法,使得最大团的个数至少加上\(1\). 题 ...

  2. 求去掉一条边使最小割变小 HAOI2017 新型城市化

    先求最小割,然后对残量网络跑Tarjan.对于所有满流的边,若其两端点不在同一个SCC中,则这条边是满足条件的. 证明见 来源:HAOI2017 新型城市化

  3. 洛谷 P3731 [HAOI2017]新型城市化【最大流(二分图匹配)+tarjan】

    我到底怎么建的图为啥要开这么大的数组啊?! 神题神题,本来以为图论出不出什么花来了. 首先要理解'团'的概念,简单来说就是无向图的一个完全子图,相关概念详见度娘. 所以关于团一般都是NP问题,只有二分 ...

  4. LOJ2276 [HAOI2017] 新型城市化 【二分图匹配】【tarjan】

    题目分析: 这题出的好! 首先问题肯定是二分图的最大独立集,如果删去某条匹配边之后独立集是否会变大. 跑出最大流之后流满的边就是匹配边. 如果一个匹配边的两个端点在一个强连通分量里,那这条边删掉之后我 ...

  5. [HAOI2017] 新型城市化

    给出的图中恰包含2个团,则图的补图为一个二分图,其最大独立集为原图的最大团. 我们知道,二分图的最大独立集=V-最小顶点覆盖,最小顶点覆盖=最大匹配. 问题转化为:计算删去后最大匹配减小的边集. 所以 ...

  6. Luogu3731 HAOI2017新型城市化(二分图匹配+强连通分量)

    将未建立贸易关系看成连一条边,那么这显然是个二分图.最大城市群即最大独立集,也即n-最大匹配.现在要求的就是删哪些边会使最大匹配减少,也即求哪些边一定在最大匹配中. 首先范围有点大,当然是跑个dini ...

  7. Luogu P3731 [HAOI2017]新型城市化

    题目显然可以转化为求每一条边对二分图最大独立集的贡献,二分图最大独立集\(=\)点数\(-\)最大匹配数,我们就有了\(50pts\)做法. 正解的做法是在原图上跑\(Tarjan\),最开始我想复杂 ...

  8. P3731 [HAOI2017]新型城市化(tarjan+网络流)

    洛谷 题意: 给出两个最大团的补图,现在要求增加一条边,使得最大最大团个数增加至少\(1\). 思路: 我们求出团的补图,问题可以转换为:对于一个二分图,选择删掉一条边,能够增大其最大独立集的点集数. ...

  9. 【题解】新型城市化 HAOI2017 网络流 二分图最大匹配 强连通分量

    Prelude 好,HAOI2017终于会做一道题了! 传送到洛谷:→_→ 传送到LOJ:←_← 本篇博客链接:(●'◡'●) Solution 首先要读懂题. 考场上我是这样想的QAQ. 我们把每个 ...

随机推荐

  1. 【项目 · Wonderland】UML设计

    团队作业---UML设计 Part 0 · 简要目录 Part 1 · 团队分工 Part 2 · UML Part 3 · 工具选择 Part 1 · 团队分工 Part 2 · UML 描述信息: ...

  2. ModuleNotFoundError No module named urllib2

    ModuleNotFoundError No module named urllib2?那么在进行编辑的来进行代码上开发,那就会出现的来代码的上错误,也是版本的上差异导致的问题.   工具/原料   ...

  3. 【转】C#中对IDisposable接口的理解

    IDisposable接口定义:定义一种释放分配的资源的方法. .NET 平台在内存管理方面提供了GC(Garbage Collection),负责自动释放托管资源和内存回收的工作,但它无法对非托管资 ...

  4. JAVA框架 Mybaits 输入和输出映射

    一.输入映射 当前端传来的参数,比较复杂,比如说用户名称.订单单号.账号信息等等.后端有可能有多个projo类对应这些信息.我们需要把这些的projo类封装成一个类似一个vo类. 通过设置字段形式关联 ...

  5. 树莓派学习笔记(4):利用yeelink实现在线硬件状态监控

    转载请注明:@小五义http://www.cnblogs.com/xiaowuyi 一.实验目的 本文实验目的是定时获取树莓派CPU的温度.占用率及内存占用率,并其结果上传到yeelink网站,实现在 ...

  6. 二,ESP8266 GPIO和SPI和定时器和串口(基于Lua脚本语言)

    https://www.cnblogs.com/yangfengwu/p/7514336.html 我们写lua用这个软件 如果点击的时候提示安装,,安装就行,,如果没有提示呢可以,按照下面链接的提示 ...

  7. AbelSu的区块链笔记

    最近几年,像比特币.以太坊.ICO.区块链等概念突然成为互联网热门话题,今天写这篇博客,也是做一些笔记,大概说一下对这个的解释和其他相关内容. 区块链: 区块链是分布式数据存储.点对点传输.共识机制. ...

  8. Linux下离线安装Docker

    Linux下离线安装Docker 一.基础环境 1.操作系统:CentOS 7.3 2.Docker版本:18.06.1 官方下载地址(打不开可能需要***) 3.百度云Docker 18.06.1地 ...

  9. R链接hive/oracle/mysql

    Linux:R连接hive用的Rhive 1,重要 1,sudo R CMD javareconf 2,sudo R 3, 1+1 是否为2 3.1,>install.packages(&quo ...

  10. Tarjan&&缩点简析

    由于昨天写计蒜客初赛的一道题,看出了是缩点,但一时忘记了另外一个叫什么s...的算法怎么写了,话说我为什么没有回去翻一下自己的blog然后今天就去学了更实用也更强力的Tarjan Tarjan的思想其 ...