HAOI2017 新型城市化 二分图的最大独立集+最大流+强连通缩点
题目链接(洛谷):https://www.luogu.org/problemnew/show/P3731
题意概述:给出一张二分图,询问删掉哪些边之后可以使这张二分图的最大独立集变大。N<=10000,0<=M<=min (150000,N(N-1)/2).
这个题首先你得总结出题意就是这个样子不然就是凉的。。。。。
二分图的最大独立集,可以想到网络流完成(定理:二分图的最大独立集=二分图点数-二分图最大匹配)。当最小割边小的时候独立集就变大了,因此问题变删掉哪些边可以让最小割变小。
这个问题就有经典的做法:先求出最小割,然后在残量网络上面跑tarjan强连通缩点,考察所有的满流的边,如果一条边满流并且两个端点不在同一个强连通分量中,那么这条边就存在与某个最小割中,删掉之后就相当于没有花代价把这条边割掉了,使得最小割变小(最大流变大)。
简单证明:
必要性:首先某一条边如果不满流,那么一定不是最小割中的边;如果一条满流边的反向边(它包含在残量网络中)在某个强连通分量中则可以视作给这条边找到了一个退流的方法,使得这条边(原图中)的起点处的流量可以绕过这条边到达这条边的终点。由于最小割是割掉之后源点汇点无法流通,这条边也显然不在最小割中。
充分性:当一条边满流并且反向边不在一个残量网络的强连通分量中的时候,这条边可能在最小割中,又由于这条边的反向边不在强连通分量中,我们没有办法找到一种方案使得这条边上的流量退去之后源点到汇点的流量不变。因此这条边一定在某个最小割之中,当这条边割掉之后,就相当于采用了这个最小割的方案,最小割变小。
注意一下一开始的建图,现在无向图上跑二分图确定好集合,如果用网络流跑二分图一定要注意连边必须是有向边,不然你会发现无向二分图跑最小流根本就不是匹配的意义。。。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<cmath>
#include<queue>
#include<set>
#include<map>
#include<vector>
#include<cctype>
using namespace std;
const int MAXN=; int N,M;
struct data{
int a,b;
friend bool operator < (data x,data y){
return x.a<y.a||x.a==y.a&&x.b<y.b;
}
}D[MAXN]; int ans;
struct graph{
static const int maxn=;
static const int maxm=;
struct edge{ int to,next; }E[maxm<<];
int first[maxn],np,color[maxn];
graph(){ np=; }
void add_edge(int u,int v){
E[++np]=(edge){v,first[u]};
first[u]=np;
}
void DFS(int i,int c){
color[i]=-c;
for(int p=first[i];p;p=E[p].next){
int j=E[p].to;
if(color[j]) continue;
DFS(j,color[i]);
}
}
}gp;
struct NET{
static const int maxn=;
static const int maxm=;
static const int inf=1e6;
struct edge{ int from,to,next,cap,flow; }E[(maxm+maxn*)<<];
int S,T,tot,first[maxn],np,cur[maxn],fl[maxn],d[maxn],gap[maxn];
int dfn[maxn],low[maxn],dfs_clock,sccno[maxn],scc_cnt,stk[maxn],top;
NET(){ np=dfs_clock=scc_cnt=; }
void add_edge(int u,int v,int c){
E[++np]=(edge){u,v,first[u],c,};
first[u]=np;
E[++np]=(edge){v,u,first[v],,};
first[v]=np;
}
void BFS(){
queue<int>q;
for(int i=;i<=tot;i++) d[i]=tot;
d[T]=; q.push(T);
while(!q.empty()){
int i=q.front(); q.pop();
for(int p=first[i];p;p=E[p].next){
int j=E[p].to;
if(d[j]==tot) d[j]=d[i]+,q.push(j);
}
}
}
int augment(){
int now=T,flow=inf;
while(now!=S){
flow=min(flow,E[fl[now]].cap-E[fl[now]].flow);
now=E[fl[now]].from;
}
now=T;
while(now!=S){
E[fl[now]].flow+=flow,E[(fl[now]-^)+].flow-=flow;
now=E[fl[now]].from;
}
return flow;
}
int ISAP(){
memcpy(cur,first,sizeof(first));
BFS();
for(int i=;i<=tot;i++) gap[d[i]]++;
int now=S,flow=;
while(d[S]<tot){
if(now==T) flow+=augment(),now=S;
bool ok=;
for(int p=cur[now];p;p=E[p].next){
int j=E[p].to;
if(E[p].cap>E[p].flow&&d[j]+==d[now]){
ok=,cur[now]=fl[j]=p,now=j;
break;
}
}
if(!ok){
int minl=tot;
for(int p=first[now];p;p=E[p].next){
int j=E[p].to;
if(E[p].cap>E[p].flow&&d[j]+<minl) minl=d[j]+;
}
if(--gap[d[now]]==) break;
gap[d[now]=minl]++;
cur[now]=first[now];
if(now!=S) now=E[fl[now]].from;
}
}
return flow;
}
void tarjan_scc(int i){
dfn[i]=low[i]=++dfs_clock;
stk[++top]=i;
for(int p=first[i];p;p=E[p].next){
if(E[p].cap==E[p].flow) continue;
int j=E[p].to;
if(dfn[j]){
if(!sccno[j]) low[i]=min(low[i],dfn[j]);
continue;
}
tarjan_scc(j);
low[i]=min(low[i],low[j]);
}
if(low[i]==dfn[i]){
scc_cnt++;
while(stk[top]!=i) sccno[stk[top--]]=scc_cnt;
sccno[stk[top--]]=scc_cnt;
}
}
}net; void data_in()
{
scanf("%d%d",&N,&M);
int x,y;
net.S=N+,net.T=N+,net.tot=N+;
for(int i=;i<=M;i++){
scanf("%d%d",&x,&y);
gp.add_edge(x,y); gp.add_edge(y,x);
}
for(int i=;i<=N;i++)
if(!gp.color[i]) gp.DFS(i,);
for(int i=;i<=M*;i+=){
x=gp.E[i].to,y=gp.E[i+].to;
if(gp.color[x]==) net.add_edge(x,y,);
else net.add_edge(y,x,);
}
for(int i=;i<=N;i++){
if(gp.color[i]==) net.add_edge(net.S,i,);
else net.add_edge(i,net.T,);
}
}
void work()
{
net.ISAP();
for(int i=;i<=net.tot;i++)
if(!net.dfn[i]) net.tarjan_scc(i);
for(int i=;i<=*M;i++){
if(net.E[i].cap>net.E[i].flow||net.E[i].flow==) continue;
int x=net.E[i].from,y=net.E[i].to;
if(net.sccno[x]!=net.sccno[y])
D[++ans]=(data){min(x,y),max(x,y)};
}
sort(D+,D+ans+);
printf("%d\n",ans);
for(int i=;i<=ans;i++)
printf("%d %d\n",D[i].a,D[i].b);
}
int main()
{
data_in();
work();
return ;
}
HAOI2017 新型城市化 二分图的最大独立集+最大流+强连通缩点的更多相关文章
- 【Luogu3731】[HAOI2017]新型城市化(网络流,Tarjan)
[Luogu3731][HAOI2017]新型城市化(网络流,Tarjan) 题面 洛谷 给定一张反图,保证原图能分成不超过两个团,问有多少种加上一条边的方法,使得最大团的个数至少加上\(1\). 题 ...
- 求去掉一条边使最小割变小 HAOI2017 新型城市化
先求最小割,然后对残量网络跑Tarjan.对于所有满流的边,若其两端点不在同一个SCC中,则这条边是满足条件的. 证明见 来源:HAOI2017 新型城市化
- 洛谷 P3731 [HAOI2017]新型城市化【最大流(二分图匹配)+tarjan】
我到底怎么建的图为啥要开这么大的数组啊?! 神题神题,本来以为图论出不出什么花来了. 首先要理解'团'的概念,简单来说就是无向图的一个完全子图,相关概念详见度娘. 所以关于团一般都是NP问题,只有二分 ...
- LOJ2276 [HAOI2017] 新型城市化 【二分图匹配】【tarjan】
题目分析: 这题出的好! 首先问题肯定是二分图的最大独立集,如果删去某条匹配边之后独立集是否会变大. 跑出最大流之后流满的边就是匹配边. 如果一个匹配边的两个端点在一个强连通分量里,那这条边删掉之后我 ...
- [HAOI2017] 新型城市化
给出的图中恰包含2个团,则图的补图为一个二分图,其最大独立集为原图的最大团. 我们知道,二分图的最大独立集=V-最小顶点覆盖,最小顶点覆盖=最大匹配. 问题转化为:计算删去后最大匹配减小的边集. 所以 ...
- Luogu3731 HAOI2017新型城市化(二分图匹配+强连通分量)
将未建立贸易关系看成连一条边,那么这显然是个二分图.最大城市群即最大独立集,也即n-最大匹配.现在要求的就是删哪些边会使最大匹配减少,也即求哪些边一定在最大匹配中. 首先范围有点大,当然是跑个dini ...
- Luogu P3731 [HAOI2017]新型城市化
题目显然可以转化为求每一条边对二分图最大独立集的贡献,二分图最大独立集\(=\)点数\(-\)最大匹配数,我们就有了\(50pts\)做法. 正解的做法是在原图上跑\(Tarjan\),最开始我想复杂 ...
- P3731 [HAOI2017]新型城市化(tarjan+网络流)
洛谷 题意: 给出两个最大团的补图,现在要求增加一条边,使得最大最大团个数增加至少\(1\). 思路: 我们求出团的补图,问题可以转换为:对于一个二分图,选择删掉一条边,能够增大其最大独立集的点集数. ...
- 【题解】新型城市化 HAOI2017 网络流 二分图最大匹配 强连通分量
Prelude 好,HAOI2017终于会做一道题了! 传送到洛谷:→_→ 传送到LOJ:←_← 本篇博客链接:(●'◡'●) Solution 首先要读懂题. 考场上我是这样想的QAQ. 我们把每个 ...
随机推荐
- js 小数计算时出现多余的数据
根据资料显示:是由于十进制换算成二进制,处理后,再由二进制换算成十进制时,造成的误差. 得出:所以(0.1+0.2)!=0.3 而是=0.30000000000000004的结果 解决方法: 参考:h ...
- PyQt5--ShowTips
# -*- coding:utf-8 -*- ''' Created on Sep 13, 2018 @author: SaShuangYiBing ''' import sys from PyQt5 ...
- node封装mysql操作
前言 node是基于异步的,因此在进行数据库查询操作的通常是通过回调来操作查询结果.但是在有了es7的async/await,基本不再需要回调了,所以本篇是基于async/await对mysql进行一 ...
- C#启动另一个应用程序并传参数
第一个程序: try { ProcessStartInfo startInfo = new ProcessStartInfo(); startInfo.FileName = "Windows ...
- Kubernetes1.91(K8s)安装部署过程(五)--安装flannel网络插件
node节点需要安装flannel网络插件才能保证所有的pod在一个局域网内通信,直接使用yum安装即可,版本是0.7.1. 1.安装flannel插件: 注意是2个node节点都需要安装,都需要修改 ...
- Mysql从库复制的2个问题
.为什么执行stop slave; 再start slave;可以继续主从关系呢? 其实执行stop slave;就是分别关闭了I/O线程(stop slave IO_THREAD;)和SQL线程(s ...
- 基于DirectX的半球形天空类的C++和C#实现
目前,天空绘制主要有三种方法:矩形天空.天空盒和球形天空. (1)矩形天空使用一个与地面垂直或呈一定夹角的矩形表示天空,用接近于天空的颜色或云彩纹理贴于矩形上.这种方法简单易行,但需要不断调整视角或观 ...
- input输入框的光标定位的问题
input输入框的光标定位的问题 在给input输入框赋值的时候,或者在我之前写模拟下拉框js组件的时候,时不时会碰到光标的小bug问题,比如键盘中的 上移或者下移操作,在浏览器中光标会先移到最前端, ...
- Python2.7-difflib
difflib主要用于比较两个序列的不同,常见于字符串的比较,可以对差异生成报告.SequenceMatcher 主要用于找两者相似部分,以及两者不同的转换方法,而 Differ 更注重于比较两者的区 ...
- 学习C#(一)
赶紧好好学学自己的C#,,要不然要给做的东西说拜拜了,,,时间紧迫,,,真担心会食言..................... 在C#中以为只要类有构造方法,,,,原来结构也有 using Syste ...