「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;
}
posted @ 2022-05-23 09:57  crashed  阅读(63)  评论(0编辑  收藏  举报