zoukankan      html  css  js  c++  java
  • 「SGU206」Roads

    题目

    点这里看题目。

    分析

    对于边 ((u_i,v_i)),如果边 ((u_j,v_j)) 在树的 (u_i)(v_i) 的路径上,那么就有 (d_jle d_i);基于树结构和非树边,我们可以得到 (d) 的偏序关系,而最终答案代价是 (|c_i-d_i|),我们需要最小化代价——可以直接上保序回归,虽然我还不会......

    其实,由于树边都是受“上界”限制,非树边都是受“下界”限制,那么对于树边,(dle c)对于非树边,(dge c)。因此,我们可以设 (delta _i) 为第 (i) 条边的变化量,答案变成了 (sum delta)。此时也就要求 (c_j-delta_jle c_i+delta_iLeftrightarrow c_j-c_ile delta_i+delta_j)

    等一下,我们再观察这个不等式的形式——两个变量,且和不小于一个定值——正是 KM 算法中对于顶标的限制!而当 (sumdelta) 得到最小值的时候,就是我们无论如何改变任何一个 (delta) 都会导致 (sum delta) 变得不优的时候——当我们取得最大权匹配的时候,由于每个点被分配了一条匹配边,而这条匹配边还在相等子图内,所以每个 (delta) 的改变都会牵动至少一个 (delta) 的改变,因此此时我们就得到了最小值。

    时间复杂度为 (O(m^3))

    小结:

    1. 这里的转化比较巧妙:首先是考虑变化的单向性,而后是观察结构,与 KM 顶标联系在一起
    2. 注意 KM 算法的性质——最大权匹配对应的也是最小顶标和。这样的对偶性很容易让人联想到网络流。

    代码

    #include <cstdio>
    #include <iostream>
    
    #define rep( i, a, b ) for( int i = (a) ; i <= (b) ; i ++ )
    #define per( i, a, b ) for( int i = (a) ; i >= (b) ; i -- )
    
    const int INF = 2e8;
    const int MAXN = 405;
    
    template<typename _T>
    void read( _T &x )
    {
        x = 0; char s = getchar(); bool f = false;
        while( s < '0' || '9' < s ) { 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' );
    }
    
    int G[MAXN][MAXN];
    int fr[MAXN], to[MAXN], wei[MAXN];
    
    int n, M, nodN;
    
    namespace Build
    {
        struct Edge
        {
            int to, nxt;
        }Graph[MAXN << 1];
    
        int head[MAXN], fath[MAXN], faE[MAXN], dep[MAXN];
        int N, cnt = 1;
    
    	void AddEdge( const int from, const int to )
    	{
    	    Graph[++ cnt].to = to, Graph[cnt].nxt = head[from];
    	    head[from] = cnt;
    	}
    	
    	void Init( const int u, const int fa )
    	{
    	    fath[u] = fa, dep[u] = dep[fa] + 1;
    	    for( int i = head[u], v ; i ; i = Graph[i].nxt )
    	        if( ( v = Graph[i].to ) ^ fa )
    	            faE[v] = i >> 1, Init( v, u );
    	}
    
        void Build()
        {
            read( N ), read( M ), nodN = N;
            rep( i, 1, M ) read( fr[i] ), read( to[i] ), read( wei[i] );
            rep( i, 1, N - 1 )
                AddEdge( fr[i], to[i] ), AddEdge( to[i], fr[i] );
            Init( 1, 0 );
            n = std :: max( N - 1, M - N + 1 );
            rep( i, N, M )
            {
                int u = fr[i], v = to[i];
                while( u ^ v )
                {
                    if( dep[u] < dep[v] ) std :: swap( u, v );
                    G[faE[u]][i - N + 1] = std :: max( G[faE[u]][i - N + 1], wei[faE[u]] - wei[i] );
                    u = fath[u];
                }
            }
        }
    }
    
    namespace Solve
    {
        int q[MAXN];
        int labX[MAXN], labY[MAXN], slk[MAXN];
        int pre[MAXN], matX[MAXN], matY[MAXN];
        bool visX[MAXN], visY[MAXN];
    
        void BFS( const int s )
        {
            if( matX[s] ) return ;
            int fin = 0, h = 1, t = 0;
            rep( i, 1, n )
            {
                visX[i] = visY[i] = false;
                slk[i] = INF, pre[i] = 0;
            }
            visX[q[++ t] = s] = true;
            while( true )
            {
                while( h <= t )
                {
                    int u = q[h ++];
                    rep( i, 1, n ) if( ! visY[i] )
                    {
                        if( labX[u] + labY[i] - G[u][i] < slk[i] )
                            slk[i] = labX[u] + labY[i] - G[u][i], pre[i] = u;
                    }
                }
                int delt = INF;
                rep( i, 1, n ) if( ! visY[i] )
                    delt = std :: min( delt, slk[i] );
                rep( i, 1, n ) if( visX[i] ) labX[i] -= delt;
                rep( i, 1, n ) if( visY[i] ) labY[i] += delt;
                rep( i, 1, n ) if( ! visY[i] )
                {
                    if( slk[i] > delt ) slk[i] -= delt;
                    else
                    {
                        visY[i] = true;
                        if( ! matY[i] ) { fin = i; break; }
                        visX[q[++ t] = matY[i]] = true, slk[i] = INF;
                    }
                }
                if( fin ) break;
            }
            while( fin )
                matY[fin] = pre[fin],
                std :: swap( matX[pre[fin]], fin );
        }
    
        void KM()
        {
            rep( i, 1, n )
            {
                matX[i] = matY[i] = 0;
                labX[i] = - INF, labY[i] = 0;
                rep( j, 1, n ) labX[j] = std :: max( labX[j], G[i][j] );
            }       
            rep( i, 1, n ) BFS( i );
            rep( i, 1, nodN - 1 )
                write( wei[i] - labX[i] ), putchar( '
    ' );
            rep( i, nodN, M )
                write( wei[i] + labY[i - nodN + 1] ), putchar( '
    ' );
        }
    }
    
    int main()
    {
        Build :: Build();
        Solve :: KM();
        return 0;
    }
    
  • 相关阅读:
    匹配@之前面的部分
    把一个数字的字符串转换为千分符的标识方式?
    下标重置
    linux的time命令

    常用正则
    正则
    PHP 菠菜木马代码
    PHP 木马代码,
    一句话的木马
  • 原文地址:https://www.cnblogs.com/crashed/p/15491136.html
Copyright © 2011-2022 走看看