Live2D

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;
}
posted @ 2020-12-07 18:04  Rainybunny  阅读(88)  评论(0编辑  收藏  举报