1. 程式人生 > 其它 >「UR #2」跳蚤公路

「UR #2」跳蚤公路

題目

點這裡看題目。

分析

迴歸本原:什麼方法可以判斷負環?Floyd 和 Bellman-ford。Floyd 太慢了這裡暫且不提。考慮到 Bellman-ford 判斷負環的原理是:

\(f_{k,u}\) 為經過 \(k\) 條邊後到 \(u\) 最短路的長度,則存在一條從起點到 \(u\)、且包含負環的路徑等價於 \(f_{n,u}<f_{n-1,u}\)

相似地,我們嘗試引入這個方法來建立關於 \(x\) 的方程。由於最終路徑總可以被表示為 \(px+q\) 的形式,我們可以設 \(g_{k,u,p}\) 表示經過 \(k\) 條邊後到達 \(u\)\(px+q\)

形式的路徑中的 \(\min q\)。這樣,負環判斷條件可以被表述為:

\[\min_k\{kx+g_{n,u,k}\}\ge \min_j \{jx+g_{n-1,u,k}\} \]

解這個不等式分為兩步:首先考慮確定的 \(j/k\) 的解集,而後根據 \(\min\) 的邏輯,通過 \(\cap,\cup\) 生成最終解集。

最後還需要注意一點,有可能存在一些路徑,包含負環但長度還超過了 \(n\),也就是不能在 Bellman-ford 中直接檢測到。這也並不難處理:由於負環本身一定會被檢測到,我們只需要處理傳遞閉包之後,將檢測出來的資訊傳遞一下即可;在本題中也就是需要將多個解集取交。

由於最終需要多次交集運算,因而最好先處理 \(k\) 確定時的解集,也就是 \(k\) 確定時對於所有 \(j\) 的解集取並,之後就只需要一路取交集了。並集則可以轉化為補集取交

小結:

  1. 步步深入。這裡我們實際上是先考慮怎麼檢測負環,再利用這種檢測方式來生成關於 \(x\) 的不等式,從而解出 \(x\)

  2. 對於負環的相關內容不熟悉。

  3. 不會解這種帶 \(\min,\max\) 的不等式。不過在這裡學一下就好了,實際上就是將 \(\min,\max\) 轉化成邏輯語言再轉化成集合運算。

程式碼

#include <cmath>
#include <cstdio>
#include <vector>
#include <utility>
#include <algorithm>

#define rep( i, a, b ) for( int i = (a) ; i <= (b) ; i ++ )
#define per( i, a, b ) for( int i = (a) ; i >= (b) ; i -- )

typedef long long LL;

const LL INF = 4e18;
const int MAXN = 105, MAXM = 10005;

template<typename _T>
void read( _T &x ) {
	x = 0; char s = getchar(); bool f = false;
	while( ! ( '0' <= s && s <= '9' ) ) { f = s == '-', s = getchar(); }
	while( '0' <= s && s <= '9' ) { x = ( x << 3 ) + ( x << 1 ) + ( s - '0' ), s = getchar(); }
	if( f ) x = -x;
}

template<typename _T>
void write( _T x ) {
	if( x < 0 ) putchar( '-' ), x = -x;
	if( 9 < x ) write( x / 10 );
	putchar( x % 10 + '0' );
}

template<typename _T>
_T Min( const _T &a, const _T &b ) {
	return a < b ? a : b;
}

template<typename _T>
_T Max( const _T &a, const _T &b ) {
	return a > b ? a : b;
}

typedef std :: pair<LL, LL> Range;

struct Edge {
	int to, nxt;
} Graph[MAXM];

std :: vector<Range> R[MAXN], proc;

bool reach[MAXN][MAXN];

LL lef[MAXN], rig[MAXN];
LL dp[2][MAXN][MAXN << 1];

int fr[MAXM], to[MAXM], coe[MAXM], wei[MAXM];

int head[MAXN], into[MAXN], cnt = 0;

int N, M;

inline void AddEdge( const int &from, const int &to ) {
	Graph[++ cnt].to = to, Graph[cnt].nxt = head[from];
	head[from] = cnt, into[to] ++;
}

inline void Upt( LL &x, const LL &v ) {
	x = Min( x, v );
}

int main() {
	read( N ), read( M );
	rep( i, 1, M ) {
		read( fr[i] ), read( to[i] );
		read( wei[i] ), read( coe[i] );
		AddEdge( fr[i], to[i] );
	}
	int pre = 1, nxt = 0;
	rep( i, 1, N ) rep( j, - N, N )
		dp[pre][i][j + N] = dp[nxt][i][j + N] = INF;
	dp[nxt][1][N] = 0;
	rep( i, 1, N ) {
		pre ^= 1, nxt ^= 1;
		rep( j, 1, N ) rep( k, -N, N )
			dp[nxt][j][k + N] = dp[pre][j][k + N];
		rep( j, 1, M ) {
			int u = fr[j], v = to[j];
			int c = coe[j], w = wei[j];
			rep( k, - i + 1, i - 1 )
				Upt( dp[nxt][v][k + c + N], dp[pre][u][k + N] + w );
		}
	}
	rep( i, 1, M ) reach[fr[i]][to[i]] = true;
	rep( k, 1, N )
		rep( i, 1, N ) if( i ^ k )
			rep( j, 1, N ) if( i ^ j && j ^ k )
				reach[i][j] |= reach[i][k] & reach[k][j];
	rep( i, 1, N )
		rep( k, -N, N ) {
			if( dp[nxt][i][k + N] > 1e15 ) continue;
			LL tmpL = - INF, tmpR = INF;
			rep( j, -N, N ) {
				if( dp[pre][i][j + N] > 1e15 ) continue;
				if( j < k )
					tmpR = Min( tmpR, ( LL ) ceil( 1.0 * ( dp[pre][i][j + N] - dp[nxt][i][k + N] ) / ( k - j ) ) );
				if( j == k ) {
					if( dp[pre][i][j + N] <= dp[nxt][i][k + N] ) {
						tmpL = INF, tmpR = - INF;
						break;
					}
				}
				if( j > k )
					tmpL = Max( tmpL, ( LL ) floor( 1.0 * ( dp[pre][i][j + N] - dp[nxt][i][k + N] ) / ( k - j ) ) );
			}
			if( tmpL < tmpR ) R[i].push_back( { tmpL, tmpR } );
		}
	rep( i, 1, N ) {
		proc.clear();
		rep( j, 1, N ) if( reach[j][i] || i == j )
			for( const Range &r : R[j] )
				proc.push_back( r );
		LL l = INF, r = - INF, lst = - INF;
		std :: sort( proc.begin(), proc.end() );
		for( int n = proc.size(), k = 0 ; k < n ; k ++ ) {
			if( ! k && proc[k].first > - INF ) { l = - INF, r = proc[k].first ; break; }
			if( lst > - INF && proc[k].first >= lst ) { l = lst, r = proc[k].first; break; }
			lst = Max( lst, proc[k].second );
		}
		if( r == - INF && lst < INF ) l = lst, r = INF;
		if( proc.empty() || l == - INF || r == INF ) puts( "-1" );
		else write( Max( 0ll, r - l + 1 ) ), putchar( '\n' );
	}
	return 0;
}