Prelude

好,HAOI2017终于会做一道题了!

传送到洛谷:→_→

传送到LOJ:←_←

本篇博客链接:(●'◡'●)


Solution

首先要读懂题。

考场上我是这样想的QAQ

我们把每个城市看作一个点,在“当前没有贸易关系”的城市之间连边。

此时,如果一个城市集合是一个城市群,那么这个城市集合中的任意两个城市之间都没有边。

因为“可以划分为两个城市群”,所以这个图是个二分图。

那么“最大城市群”就是二分图的最大独立集。

“在两个城市之间建立贸易关系”即删除这两个点之间的边。

所以题目实际上是,给一个二分图,问删掉哪些边之后,最大独立集的大小会增加。

考虑如何求最大独立集大小。

最大独立集大小=总点数-最小覆盖集大小=最大匹配数。

也就是说,这个题问的是,给一个二分图,问删掉哪些边之后,最大匹配的数量会减少,也就是问,哪些边一定在最大匹配里。

这个时候,我们已经得到了50分做法了。

先建出网络流,求出最大匹配数量,然后删掉一条边重新跑一次,看最大匹配是否减少,就是我考场上的做法。

用退流可以做到更优越的复杂度,但好像过不了n=500的点?

接下来考虑满分做法。

考虑如下定理:若一条边一定在最大匹配中,则在最终的残量网络中,这条边一定满流,且这条边的两个顶点一定不在同一个强连通分量中。

证明也很简单:首先满流的要求是很显然的,其次,如果这两个点在同一个强连通分量中,那么一定有一个环经过这条边,沿着环增广一下,网络仍然满足流量限制,但是这条边就不满流了,于是就得到了一组新的最大匹配。

所以只要跑完Dinic跑Tarjan就好了。


Code

#include <cstring>
#include <cstdio>
#include <algorithm>
#include <queue>
#include <stack>
#include <vector>
#include <utility> using namespace std;
typedef pair<int,int> pii;
const int MAXN = 10010;
const int MAXM = 150010;
const int MAXV = 100010;
const int MAXE = 1000010;
const int INF = 0x3f3f3f3f;
int _w; int n, m, uu[MAXM], vv[MAXM]; namespace G {
int head[MAXN], nxt[MAXM<<1], to[MAXM<<1], eid;
void init() {
eid = 0;
memset(head, -1, sizeof head);
}
void adde( int u, int v ) {
to[eid] = v, nxt[eid] = head[u], head[u] = eid++;
to[eid] = u, nxt[eid] = head[v], head[v] = eid++;
}
} namespace Dinic {
struct Edge {
int u, v, c, f;
Edge() {}
Edge( int u, int v, int c, int f ):
u(u), v(v), c(c), f(f) {}
}; int n, m, s, t;
int head[MAXV], nxt[MAXE<<1];
Edge edge[MAXE<<1];
int dis[MAXV], cur[MAXV];
queue<int> q; void init( int _n ) {
n = _n, m = 0;
for( int i = 0; i < n; ++i )
head[i] = -1;
}
int adde( int u, int v, int c ) {
int eid = m;
edge[m] = Edge(u, v, c, 0);
nxt[m] = head[u], head[u] = m++;
edge[m] = Edge(v, u, 0, 0);
nxt[m] = head[v], head[v] = m++;
return eid;
}
bool bfs() {
for( int i = 0; i < n; ++i )
dis[i] = INF;
dis[s] = 0, q.push(s);
while( !q.empty() ) {
int u = q.front(); q.pop();
for( int i = head[u]; ~i; i = nxt[i] ) {
Edge &e = edge[i];
if( e.c > e.f && dis[e.v] == INF ) {
dis[e.v] = dis[u] + 1;
q.push(e.v);
}
}
}
return dis[t] != INF;
}
int dfs( int u, int res ) {
if( u == t || !res ) return res;
int flow = 0;
for( int &i = cur[u]; ~i; i = nxt[i] ) {
Edge &e = edge[i];
if( e.c > e.f && dis[e.v] == dis[u] + 1 ) {
int f = dfs( e.v, min(res, e.c-e.f) );
flow += f, res -= f;
e.f += f, edge[i^1].f -= f;
if( !res ) break;
}
}
return flow;
}
int solve( int _s, int _t ) {
s = _s, t = _t;
int flow = 0;
while( bfs() ) {
for( int i = 0; i < n; ++i )
cur[i] = head[i];
flow += dfs(s, INF);
}
return flow;
}
} namespace Bipartite {
int color[MAXN], eid[MAXM];
queue<int> q; void bfs( int s ) {
using namespace G; color[s] = 0, q.push(s);
while( !q.empty() ) {
int u = q.front(); q.pop();
for( int i = head[u]; ~i; i = nxt[i] ) {
int v = to[i];
if( color[v] == -1 ) {
color[v] = !color[u];
q.push(v);
}
}
}
}
void bipartite() {
for( int i = 1; i <= n; ++i )
color[i] = -1;
for( int i = 1; i <= n; ++i )
if( color[i] == -1 )
bfs(i);
int s = 0, t = n+1;
Dinic::init(t+1);
for( int i = 1; i <= n; ++i )
if( color[i] ) Dinic::adde(s, i, 1);
else Dinic::adde(i, t, 1);
for( int i = 0; i < m; ++i )
if( color[uu[i]] )
eid[i] = Dinic::adde( uu[i], vv[i], 1 );
else
eid[i] = Dinic::adde( vv[i], uu[i], 1 );
Dinic::solve(s, t);
}
}
using Bipartite::bipartite; namespace Tarjan {
using namespace Dinic; int dfn[MAXV], low[MAXV], scc[MAXV], dfnc, sccc;
stack<int> stk; void dfs( int u ) {
dfn[u] = low[u] = ++dfnc;
stk.push(u);
for( int i = head[u]; ~i; i = nxt[i] ) {
Edge &e = edge[i];
if( e.c == e.f ) continue;
int v = e.v;
if( !dfn[v] ) {
dfs(v);
low[u] = min( low[u], low[v] );
} else if( !scc[v] ) {
low[u] = min( low[u], dfn[v] );
}
}
if( low[u] == dfn[u] ) {
++sccc;
while(1) {
int o = stk.top(); stk.pop();
scc[o] = sccc;
if( o == u ) break;
}
}
}
void tarjan() {
dfnc = sccc = 0;
for( int i = 0; i < Dinic::n; ++i )
if( !dfn[i] ) dfs(i);
}
}
using Tarjan::tarjan; namespace Solve {
vector<pii> ans;
void solve() {
using Dinic::Edge;
using Dinic::edge;
using Tarjan::scc;
using Bipartite::eid; for( int i = 0; i < m; ++i ) {
Edge &e = edge[eid[i]];
if( e.c != e.f ) continue;
int u = e.u, v = e.v;
if( u > v ) swap(u, v);
if( scc[u] == scc[v] ) continue;
ans.push_back( pii(u, v) );
}
sort(ans.begin(), ans.end());
printf( "%lu\n", ans.size() );
for( int i = 0; i < (int)ans.size(); ++i )
printf( "%d %d\n", ans[i].first, ans[i].second );
}
}
using Solve::solve; int main() {
_w = scanf( "%d%d", &n, &m );
G::init();
for( int i = 0; i < m; ++i ) {
_w = scanf( "%d%d", uu+i, vv+i );
G::adde( uu[i], vv[i] );
}
bipartite();
tarjan();
solve();
return 0;
}

【题解】新型城市化 HAOI2017 网络流 二分图最大匹配 强连通分量的更多相关文章

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

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

  2. poj1904 二分图匹配+强连通分量

    http://poj.org/problem?id=1904 Description Once upon a time there lived a king and he had N sons. An ...

  3. UESTC 898 方老师和缘分 --二分图匹配+强连通分量

    这题原来以为是某种匹配问题,后来好像说是强连通的问题. 做法:建图,每个方老师和它想要的缘分之间连一条有向边,然后,在给出的初始匹配中反向建边,即如果第i个方老师现在找到的是缘分u,则建边u-> ...

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

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

  5. HDU2389 Rain on your Parade —— 二分图最大匹配 HK算法

    题目链接:https://vjudge.net/problem/HDU-2389 Rain on your Parade Time Limit: 6000/3000 MS (Java/Others)  ...

  6. HAOI2017 新型城市化 二分图的最大独立集+最大流+强连通缩点

    题目链接(洛谷):https://www.luogu.org/problemnew/show/P3731 题意概述:给出一张二分图,询问删掉哪些边之后可以使这张二分图的最大独立集变大.N<=10 ...

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

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

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

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

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

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

随机推荐

  1. 【quickhybrid】JS端的项目实现

    前言 API实现阶段之JS端的实现,重点描述这个项目的JS端都有些什么内容,是如何实现的. 不同于一般混合框架的只包含JSBridge部分的前端实现,本框架的前端实现包括JSBridge部分.多平台支 ...

  2. H-ui框架制作选项卡

    本案例运用H-ui框架,写了一个选项卡案例 1. html代码(固定这样写,用一个div包裹控制条tabBar和内容条tabCon) <div id="tab-index-carteg ...

  3. 《TCP/IP 详解 卷1:协议》第 4 章:地址解析协议

    链路层是经过单一链路通信的协议层. IP 网络层协议的设计目标是为跨越不同物理类型的.多节点网络的 packet ,提供主机寻址.路由操作. 在其中要注意的一点是:网络层使用的地址和底层网络硬件使用的 ...

  4. profibus总线和profibus dp的区别

    profibus总线和profibus dp的区别:PROFBUS是一种国际性的开放式的现场总线标准,它既可以用于高速并且对于时间苛求的数据传输,也可以用于大范围的复杂通讯场合.PROFBUS-DP是 ...

  5. Enterprise Library 2.0 参考源码索引

    http://www.projky.com/entlib/2.0/Microsoft/Practices/EnterpriseLibrary/Caching/BackgroundScheduler.c ...

  6. 0603团队变化+sprint第二个冲刺

    开始一个新的冲刺: 起止:2016.6.1~2016.6.14 按照以下过程进行 ProductBacklog:继续向下细化 Sprint 计划会议:确定此次冲刺要完成的目标 Sprint Backl ...

  7. 在Delphi中通过OLE方式写Excel文件

    报表的打印是每个项目都会遇到的问题.由于报表格式要求五花八门,往往又同时要求打印格式可方便调整.作为一种替代方法,可以将需要打印的报表导出到Excel/Word,打印交给Office去吧.由于Offi ...

  8. 软盘相关知识和通过BIOS中断访问软盘

    一. 软盘基础知识介绍 (1) 3.5英寸软盘 3.5英寸软盘分为上下两面,每面有80个磁道,每个磁道又分为18个扇区,每个扇区大小为512个字节. 软盘大小计算: 2面 * 80磁道 * 18扇区 ...

  9. 在Google Chrome中快速解除网页屏蔽鼠标右键、复制等限制

    第一步,将书签栏设置为显示状态!   第二步,添加新书签——>在标签栏点击右键,选择“添加网页”. 第三步,设置新书签的内容. 1.起名.这个凭个人爱好吧 2.网址栏输入: javascript ...

  10. onMeasure实例分析

    本文转自:http://blog.csdn.net/u012604322/article/details/17097105           上面这个两个视图是Android API中没有给出来的但 ...