传送门:【ACdream】1223 Roads Andrew Stankevich Contest 2
题目分析:KM果题啊。。。。
这题就是考察KM匹配的性质:KM匹配中任何时刻满足dx[i]+dy[j]>=w[i][j],其中w[i][j]是边(i,j)的权值,dx,dy是可行顶标的权值。而且最大权匹配结束以后一定是完备匹配且所有顶标之和一定最小。
题目要求保留所有的石头路,而所有的石头路构成一棵树。首先dfs一遍石头路建树,然后每添加一条编号为j的普通路时一定会和石头路成环,那么这个环上编号为i的石头路和其的关系为修改边权以后满足:c[i]-d[i]<=c[j]+d[j]。其中c[i]为路初始的权值,d[i]为路改变权值的幅度。为什么一定是石头路减而普通路加?想想也明白石头路加权值或者普通路减权值不过是白费力气,还可能增大花费。
那么我们对c[i]-d[i]<=c[j]+d[j]稍作变形可得d[i]+d[j]>=c[i]-c[j],可以发现右边的c[i]和c[j]都是给定的,是常数!设w[i][j] = c[i] - c[j],则d[i]+d[j]>=c[i]-c[j] => d[i]+d[j]>=w[i][j]。这不正是KM匹配的基本性质吗?于是我们求一次最大权匹配自然就得到了满足题目要求的顶标。
对于KM算法我并不了解,昨天因为这道题才开始看。。以前都是用费用流写KM的。。。这次因为普通的费用流不维护顶标,所以才打算看掉KM的,翻翻白书才感到很蛋疼,竟然发现dx[i]+dy[j]>=w[i][j]竟然就是KM的基本性质= =。接下来应该学学原始-对偶了,不知道能不能找到好点的资料。
不知道KM算法还有什么地方能优化的,先放放。。
代码如下:
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
using namespace std ;
typedef long long LL ;
#pragma comment(linker, "/STACK:16777216")
#define rep( i , a , b ) for ( int i = a ; i < b ; ++ i )
#define For( i , a , b ) for ( int i = a ; i <= b ; ++ i )
#define rev( i , a , b ) for ( int i = a ; i >= b ; -- i )
#define travel( e , H , u ) for ( Edge* e = H[u] ; e ; e = e -> next )
#define clr( a , x ) memset ( a , x , sizeof a )
#define cpy( a , x ) memcpy ( a , x , sizeof a )
const int MAXN = 405 ;
const int MAXE = 233 ;
const int INF = 0x3f3f3f3f ;
struct Edge {
int v , c , idx ;
Edge* next ;
} E[MAXE] , *H[MAXN] , *edge , *idx[MAXN] ;
int dep[MAXN] ;
int pre[MAXN] ;
int G[MAXN][MAXN] ;
int visx[MAXN] , visy[MAXN] , Time ;
int Lx[MAXN] , Ly[MAXN] ;
int dx[MAXN] , dy[MAXN] ;
int slack[MAXN] ;
int cost[MAXN] ;
int n , m , K , V ;
void clear () {
edge = E ;
clr ( H , 0 ) ;
dep[1] = pre[1] = 0 ;
}
void addedge ( int u , int v , int c , int idx ) {
edge->v = v ;
edge->c = c ;
edge->idx = idx ;
edge->next = H[u] ;
H[u] = edge ++ ;
}
int dfs ( int u ) {
travel ( e , H , u ) {
int v = e->v ;
if ( v == pre[u] ) continue ;
dep[v] = dep[u] + 1 ;
pre[v] = u ;
idx[v] = e ;
dfs ( v ) ;
}
}
void process ( int x , int y , int cv , int v ) {
while ( x != y ) {
if ( dep[x] < dep[y] ) swap ( x , y ) ;
int u = idx[x]->idx , cu = idx[x]->c ;
if ( cv < cu ) G[u][v] = cu - cv ;
x = pre[x] ;
}
}
int find ( int u ) {
visx[u] = Time ;
For ( v , 1 , m ) {
if ( visy[v] == Time ) continue ;
int tmp = dx[u] + dy[v] - G[u][v] ;
if ( !tmp ) {
visy[v] = Time ;
if ( Ly[v] == -1 || find ( Ly[v] ) ) {
Lx[u] = v ;
Ly[v] = u ;
return 1 ;
}
} else if ( slack[v] > tmp ) slack[v] = tmp ;
}
return 0 ;
}
void KM () {
clr ( Lx , -1 ) ;
clr ( Ly , -1 ) ;
clr ( dx , -INF ) ;
clr ( dy , 0 ) ;
rep ( i , 1 , n ) For ( j , 1 , m ) if ( G[i][j] > dx[i] ) dx[i] = G[i][j] ;
rep ( i , 1 , n ) {
For ( j , 1 , m ) slack[j] = INF ;
while ( 1 ) {
++ Time ;
if ( find ( i ) ) break ;
int d = INF ;
For ( j , 1 , m ) if ( visy[j] != Time && d > slack[j] ) d = slack[j] ;
rep ( j , 1 , n ) if ( visx[j] == Time ) dx[j] -= d ;
For ( j , 1 , m ) visy[j] == Time ? dy[j] += d : slack[j] -= d ;
}
}
}
void solve () {
int u , v , c ;
clear () ;
clr ( G , 0 ) ;
m = max ( n - 1 , K - ( n - 1 ) ) ;
rep ( i , 1 , n ) {
scanf ( "%d%d%d" , &u , &v , &c ) ;
addedge ( u , v , c , i ) ;
addedge ( v , u , c , i ) ;
cost[i] = c ;
}
dfs ( 1 ) ;
For ( i , n , K ) {
scanf ( "%d%d%d" , &u , &v , &c ) ;
process ( u , v , c , i - ( n - 1 ) ) ;
cost[i] = c ;
}
KM () ;
For ( i , 1 , K ) printf ( "%d\n" , i < n ? cost[i] - dx[i] : cost[i] + dy[i - ( n - 1 )] ) ;
}
int main () {
clr ( visx , 0 ) ;
clr ( visy , 0 ) ;
Time = 0 ;
while ( ~scanf ( "%d%d" , &n , &K ) ) solve () ;
return 0 ;
}