「SGU206」Roads

题目

点这里看题目。

分析

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

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

等一下,我们再观察这个不等式的形式——两个变量,且和不小于一个定值——正是 KM 算法中对于顶标的限制!而当 \(\sum\delta\) 得到最小值的时候,就是我们无论如何改变任何一个 \(\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( '\n' );
        rep( i, nodN, M )
            write( wei[i] + labY[i - nodN + 1] ), putchar( '\n' );
    }
}

int main()
{
    Build :: Build();
    Solve :: KM();
    return 0;
}
posted @ 2021-10-31 22:04  crashed  阅读(67)  评论(0编辑  收藏  举报