题目
点这里看题目。
分析
对于边 ((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))。
小结:
- 这里的转化比较巧妙:首先是考虑变化的单向性,而后是观察结构,与 KM 顶标联系在一起;
- 注意 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;
}