Solution -「POI 2010」「洛谷 P3511」MOS-Bridges
\(\mathcal{Description}\)
Link.(洛谷上这翻译真的一言难尽呐。
给定一个 \(n\) 个点 \(m\) 条边的无向图,一条边 \((u,v,a,b)\) 表示从 \(u\) 到 \(v\) 的代价为 \(a\),\(v\) 到 \(u\) 的代价为 \(b\)。求从结点 \(1\) 开始的,经过每个点至少一次,每条边恰好一次,最后回到结点 \(1\) 的路径,使得每条边代价的最大值最小。
\(n,a,b\le10^3\),\(m\le2\times10^4\)。
\(\mathcal{Solution}\)
二分嘛,考虑如何 check。
不妨设每条边 \((u,v,a,b)\) 都有 \(a\le b\),那么能经过每条边,必然能经过每条 \(\langle u,v\rangle\)。直接如此钦定所有边的方向,现在就是要考虑能够翻转某些边使得边构成有向欧拉回路,即每个点出入度相等。
记 \(\Delta(u)=\operatorname{ind}(u)-\operatorname{outd}(u)\),显然一条合法的反向边 \(\langle v,u\rangle\) 替换入路径会使 \(\Delta(u)\leftarrow\Delta(u)+1,\Delta(v)\leftarrow\Delta(v)-1\),那这就是一个网络流模型:\(S\) 连向 \(u\)(\(\Delta(u)>0\)),流量为 \(\Delta(u)-\frac{\operatorname{deg}(u)}2\),反向边则体现为 \(v\) 到 \(u\),流量为 \(1\) 的边,最后 \(v\)(\(\Delta(v)<0\))连向 \(T\),流量为 \(\frac{\operatorname{deg}(v)}2-\Delta(v)\),检查最大流是否为 \(u\) 的总数即可。
复杂度 \(\mathcal O(D\log\max\{b\})\),其中 \(D\) 为 Dinic 算法对于此图的复杂度(即我不知道但可过的复杂度)。
\(\mathcal{Code}\)
/* Clearink */
#include <queue>
#include <cstdio>
#include <vector>
#include <assert.h>
#include <algorithm>
inline int rint () {
int x = 0; char s = getchar ();
for ( ; s < '0' || '9' < s; s = getchar () );
for ( ; '0' <= s && s <= '9'; s = getchar () ) x = x * 10 + ( s ^ '0' );
return x;
}
inline void wint ( const int x ) {
if ( 9 < x ) wint ( x / 10 );
putchar ( x % 10 ^ '0' );
}
inline int imin ( const int a, const int b ) { return a < b ? a : b; }
const int MAXN = 10000, MAXM = 2e5, INF = 0x3f3f3f3f;
int n, m, rev[MAXN + 5], deg[MAXN + 5];
bool conn[MAXN + 5]; // some nodes must be connected with 1.
struct Edge { int u, v, a, b; } edge[MAXM + 5];
struct DSU {
int fa[MAXN + 5];
inline void clear () { for ( int i = 1; i <= n; ++i ) fa[i] = i; }
inline int find ( const int x ) { return x ^ fa[x] ? fa[x] = find ( fa[x] ) : x; }
inline void unite ( int x, int y ) { fa[find ( x )] = find ( y ); }
} dsu;
struct MaxFlowGraph {
int ecnt, head[MAXN + 5], S, T;
int curh[MAXN + 5], d[MAXN + 5];
struct Edge { int to, flow, nxt; } graph[MAXM * 2 + MAXN * 2 + 5];
inline void clear () {
ecnt = 1;
for ( int i = S; i <= T; ++i ) head[i] = 0;
}
inline void link ( const int s, const int t, const int f ) {
graph[++ecnt] = { t, f, head[s] };
head[s] = ecnt;
}
inline void addEdge ( const int s, const int t, const int f ) {
link ( s, t, f ), link ( t, s, 0 );
}
inline bool BFS () {
static std::queue<int> que;
for ( int i = S; i <= T; ++i ) d[i] = -1;
d[S] = 0, que.push ( S );
while ( !que.empty () ) {
int u = que.front (); que.pop ();
for ( int i = head[u], v; i; i = graph[i].nxt ) {
if ( graph[i].flow && !~d[v = graph[i].to] ) {
d[v] = d[u] + 1;
que.push ( v );
}
}
}
return ~d[T];
}
inline int DFS ( const int u, const int iflow ) {
if ( u == T ) return iflow;
int ret = 0;
for ( int& i = curh[u], v; i; i = graph[i].nxt ) {
if ( graph[i].flow && d[v = graph[i].to] == d[u] + 1 ) {
int oflow = DFS ( v, imin ( iflow - ret, graph[i].flow ) );
ret += oflow, graph[i].flow -= oflow, graph[i ^ 1].flow += oflow;
if ( ret == iflow ) break;
}
}
if ( !ret ) d[u] = -1;
return ret;
}
inline int Dinic () {
int ret = 0;
for ( ; BFS (); ret += DFS ( S, INF ) ) {
for ( int i = S; i <= T; ++i ) curh[i] = head[i];
}
return ret;
}
} mfg;
inline bool check ( const int lim ) {
mfg.clear ();
for ( int i = 1; i <= m; ++i ) {
if ( edge[i].b <= lim ) {
mfg.addEdge ( edge[i].v, edge[i].u, 1 );
rev[i] = mfg.ecnt ^ 1; // `addEdge' let ecnt+=2.
}
}
int out = 0;
for ( int i = 1; i <= n; ++i ) {
if ( deg[i] > 0 ) out += deg[i] >> 1, mfg.addEdge ( mfg.S, i, deg[i] >> 1 );
if ( deg[i] < 0 ) mfg.addEdge ( i, mfg.T, -deg[i] >> 1 );
}
return mfg.Dinic () == out;
}
struct FinalGraph {
int ecnt, head[MAXN + 5];
struct Edge { int to, nxt; } graph[MAXM + 5];
inline void link ( const int s, const int t ) {
graph[++ecnt] = { t, head[s] };
head[s] = ecnt;
}
inline void findEC ( const int u, std::vector<int>& res ) {
for ( int& i = head[u], t; i; ) {
i = graph[t = i].nxt;
findEC ( graph[t].to, res );
res.push_back ( t );
}
}
} fg;
inline void print ( const int lim ) {
assert ( check ( lim ) ); // to get the rest flow.
for ( int i = 1; i <= m; ++i ) {
if ( edge[i].b > lim || mfg.graph[rev[i]].flow ) {
fg.link ( edge[i].u, edge[i].v );
} else {
fg.link ( edge[i].v, edge[i].u );
}
}
std::vector<int> ans;
fg.findEC ( 1, ans );
assert ( ( int ) ans.size () == m ); // is the graph connected?
wint ( lim ), putchar ( '\n' );
for ( int i = ans.size () - 1; ~i; --i ) {
wint ( ans[i] ), putchar ( i ? ' ' : '\n' );
}
}
int main () {
n = rint (), m = rint ();
mfg.S = 0, mfg.T = n + 1;
int l = 0, r = 0;
conn[1] = true, dsu.clear ();
for ( int i = 1, u, v, a, b; i <= m; ++i ) {
u = rint (), v = rint (), a = rint (), b = rint ();
conn[u] = conn[v] = true, dsu.unite ( u, v );
if ( a > b ) a ^= b ^= a ^= b, u ^= v ^= u ^= v;
--deg[u], ++deg[v];
edge[i] = { u, v, a, b };
l = a > l ? a : l, r = b > r ? b : r;
}
for ( int i = 1; i <= n; ++i ) {
if ( conn[i] && ( deg[i] & 1 || dsu.find ( i ) ^ dsu.find ( 1 ) ) ) {
return puts ( "NIE" ), 0;
}
}
int t = ++r;
while ( l < r ) {
int mid = l + r >> 1;
if ( check ( mid ) ) r = mid;
else l = mid + 1;
}
if ( l == t ) return puts ( "NIE" ), 0;
print ( l );
return 0;
}
Solution -「POI 2010」「洛谷 P3511」MOS-Bridges的更多相关文章
- 「区间DP」「洛谷P1043」数字游戏
「洛谷P1043」数字游戏 日后再写 代码 /*#!/bin/sh dir=$GEDIT_CURRENT_DOCUMENT_DIR name=$GEDIT_CURRENT_DOCUMENT_NAME ...
- Solution -「JSOI 2019」「洛谷 P5334」节日庆典
\(\mathscr{Description}\) Link. 给定字符串 \(S\),求 \(S\) 的每个前缀的最小表示法起始下标(若有多个,取最小的). \(|S|\le3\time ...
- Solution -「洛谷 P4372」Out of Sorts P
\(\mathcal{Description}\) OurOJ & 洛谷 P4372(几乎一致) 设计一个排序算法,设现在对 \(\{a_n\}\) 中 \([l,r]\) 内的元素排 ...
- Solution -「APIO 2016」「洛谷 P3643」划艇
\(\mathcal{Description}\) Link & 双倍经验. 给定 \(n\) 个区间 \([a_i,b_i)\)(注意原题是闭区间,这里只为方便后文描述),求 \(\ ...
- 「洛谷4197」「BZOJ3545」peak【线段树合并】
题目链接 [洛谷] [BZOJ]没有权限号嘤嘤嘤.题号:3545 题解 窝不会克鲁斯卡尔重构树怎么办??? 可以离线乱搞. 我们将所有的操作全都存下来. 为了解决小于等于\(x\)的操作,那么我们按照 ...
- 「洛谷3338」「ZJOI2014」力【FFT】
题目链接 [BZOJ] [洛谷] 题解 首先我们需要对这个式子进行化简,否则对着这么大一坨东西只能暴力... \[F_i=\sum_{j<i} \frac{q_iq_j}{(i-j)^2}-\s ...
- 「BZOJ2733」「洛谷3224」「HNOI2012」永无乡【线段树合并】
题目链接 [洛谷] 题解 很明显是要用线段树合并的. 对于当前的每一个连通块都建立一个权值线段树. 权值线段树处理操作中的\(k\)大的问题. 如果需要合并,那么就线段树暴力合并,时间复杂度是\(nl ...
- 「洛谷3870」「TJOI2009」开关【线段树】
题目链接 [洛谷] 题解 来做一下水题来掩饰ZJOI2019考炸的心情QwQ. 很明显可以线段树. 维护两个值,\(Lazy\)懒标记表示当前区间是否需要翻转,\(s\)表示区间还有多少灯是亮着的. ...
- 「洛谷5300」「GXOI/GZOI2019」与或和【单调栈+二进制转化】
题目链接 [洛谷传送门] 题解 按位处理. 把每一位对应的图都处理出来 然后单调栈处理一下就好了. \(and\)操作处理全\(1\). \(or\)操作处理全\(0\). 代码 #include & ...
随机推荐
- [Beyond Compare] 排除/忽略 .svn 文件夹
[Beyond Compare] Exclude .svn folders Beyond Compare 3 Session >> Session Settings... >> ...
- 【Spring专场】「AOP容器」不看源码就带你认识核心流程以及运作原理
前提回顾 前一篇文章主要介绍了spring核心特性机制的IOC容器机制和核心运作原理,接下来我们去介绍另外一个较为核心的功能,那就是AOP容器机制,主要负责承接前一篇代理模式机制中动态代理:JDKPr ...
- 百度地图BMap实现在行政区域内做标注
使用环境 vue bmap.js element-ui 页面展示 前提步骤 在index中引入百度地图提供的js库 在使用的vue页面中实例化地图 <!-- 给id随便起给名字 --> & ...
- 单片机main函数退出后发生什么——以stm32为例
STM32:main函数退出后发生什么? 我们都在说单片机要运行在无限循环里,不能退出,可退出之后会发生什么? 讨论STM32启动过程的文章数不胜数,可main函数结束之后会发生什么却少有讨论. 几日 ...
- thanos receiver压测结果分享
压测环境 k8s容器环境 单核 32GB内存 thanos版本:v0.22.0 客户端发送32271959条数据,100个并发:同样的数据文件发送了两遍,取第二遍的性能数据 在thanos recei ...
- 【记录一个问题】redis中执行事务出现错误“EXECABORT Transaction discarded because of previous errors”
执行事务的大致代码如下: redisClient := GetRedisClient() pipe := redisClient.TxPipeline() err := pipe.ZAdd(k, ar ...
- 使用Flightradar24's CesiumJS App追踪世界商用航线
Cesium中文网:http://cesiumcn.org/ | 国内快速访问:http://cesium.coinidea.com/ 每天,超过10万架商业航班在世界各地运送乘客.在任何特定时刻,您 ...
- 工作自动化,替代手工操作,使用python操作MFC、windows程序
目录 背景--为什么要自动化操作? 方法--怎么实现自动化操作? 查找窗体 发送消息 获取文本 总结 背景--为什么要自动化操作? 工作中总是遇到反复重复性的工作?怎么用程序把它变成自动化操作?将程序 ...
- Linux下的ssh、scala、spark配置
注:笔记旨在记录,配置方式每个人多少有点不同,但大同小异,以下是个人爱好的配置方式. 目录 一.配置jdk 二.配置ssh 三.配置hadoop 四.配置scala 五.配置spark 平台:win1 ...
- Gc如何判断对象可以被回收?
Gc如何判断对象可以被回收? 1 引用计数器 引用计数法的算法思路:给对象增加一个引用计数器,每当对象增加一个引用计数器+1,失去一个引用-1,所以当计数器是0的时候对象就没有引用了,就会被认为可回收 ...