「UR #2」跳蚤公路
阿新 • • 發佈:2022-05-23
題目
點這裡看題目。
分析
迴歸本原:什麼方法可以判斷負環?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\)
解這個不等式分為兩步:首先考慮確定的 \(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; }