• 「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;
    }
    
  • 相关阅读:
    继承 接口 多态
    组合(补充)和 继承
    面向对象初级
    模块和包
    time,random,os,sys,序列化模块
    inline详解
    C++静态数据成员与静态成员函数
    OpenCV Mat数据类型及位数总结(转载)
    拼搏奋斗类
    c++虚函数实现机制(转)
  • 原文地址:https://www.cnblogs.com/crashed/p/16300130.html
Copyright © 2020-2023  润新知