题目
点这里看题目。
分析
回归本原:什么方法可以判断负环?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\)。这样,负环判断条件可以被表述为:
解这个不等式分为两步:首先考虑确定的 \(j/k\) 的解集,而后根据 \(\min\) 的逻辑,通过 \(\cap,\cup\) 生成最终解集。
最后还需要注意一点,有可能存在一些路径,包含负环但长度还超过了 \(n\),也就是不能在 Bellman-ford 中直接检测到。这也并不难处理:由于负环本身一定会被检测到,我们只需要处理传递闭包之后,将检测出来的信息传递一下即可;在本题中也就是需要将多个解集取交。
由于最终需要多次交集运算,因而最好先处理 \(k\) 确定时的解集,也就是 \(k\) 确定时对于所有 \(j\) 的解集取并,之后就只需要一路取交集了。并集则可以转化为补集取交。
小结:
-
步步深入。这里我们实际上是先考虑怎么检测负环,再利用这种检测方式来生成关于 \(x\) 的不等式,从而解出 \(x\)。
-
对于负环的相关内容不熟悉。
-
不会解这种带 \(\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;
}