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;
    }
    
  • 相关阅读:
    POJ 3258 (NOIP2015 D2T1跳石头)
    POJ 3122 二分
    POJ 3104 二分
    POJ 1995 快速幂
    409. Longest Palindrome
    389. Find the Difference
    381. Insert Delete GetRandom O(1)
    380. Insert Delete GetRandom O(1)
    355. Design Twitter
    347. Top K Frequent Elements (sort map)
  • 原文地址:https://www.cnblogs.com/crashed/p/15491136.html
Copyright © 2011-2022 走看看