Description
有n个变量w[1]~w[n],每个变量可以取W或-W。
有p个式子,形如Hi=ai|w[xi]-w[yi]|+bi|w[yi]-w[zi]|+ci|w[zi]-w[xi]|+di(w[xi]-w[yi])+ei(w[yi]-w[zi])+fi(w[zi]-w[xi])。
有q个条件,形如w[x]<=w[y]或w[x]=w[y]或w[x]<w[y]。
最小化sigma(wi)+sigma(Hi)。
对于100%的数据,t<=10,n<=500,p,q<=1000,W<=1e6,系数<=1e3, 所有数字均为非负整数。
Solution
简单做一下总结,有关这一类二元组的选择以及关系的建图。
首先我们需要建的是最小割的模型而不是费用流(
每个变量一个点,以及源汇两点共 n + 2 n+2 n + 2 个点。
对于每一个变量有 0 , 1 0,1 0 , 1 两种状态,对应图上与 S , T S,T S , T 相连的两条边,割掉 ( S , x ) (S,x) ( S , x ) 即为选1,反之割掉 ( x , T ) (x,T) ( x , T ) 即为选择0。
流量为选择 0 / 1 0/1 0 / 1 的贡献。
a x = 0 , a y = 1 a_x=0,a_y=1 a x = 0 , a y = 1 有 v v v 贡献: ( y , x , v ) (y,x,v) ( y , x , v )
a x = a y = 0 a_x=a_y=0 a x = a y = 0 有 v v v 贡献: ( S , n e w , v ) , ( n e w , x , i n f ) , ( n e w , y , i n f ) (S,new,v),(new,x,inf),(new,y,inf) ( S , n e w , v ) , ( n e w , x , i n f ) , ( n e w , y , i n f ) 。
a x = a y = 1 a_x=a_y=1 a x = a y = 1 有 v v v 贡献: ( x , n e w , i n f ) , ( y , n e w , i n f ) , ( n e w , T , v ) (x,new,inf),(y,new,inf),(new,T,v) ( x , n e w , i n f ) , ( y , n e w , i n f ) , ( n e w , T , v ) 。
a x a_x a x 与 a y a_y a y 之间的限制可以考虑将上面的 v v v 变成 i n f inf i n f 对应加边。
跑最小割即可得到最小贡献。
最大贡献可以考虑求上面所有限制的补集,然后从总的里面减去一个最小割。
我的辣鸡当前弧
for ( int & i= cur[ x] ; i&& res; i= nx[ i] ) if ( ec[ i] > 0 && dis[ x] + 1 == dis[ e[ i] ] ) {
ll tmp= dg ( e[ i] , min ( res, ec[ i] ) ) ;
ec[ i] - = tmp, ec[ i^ 1 ] + = tmp, res- = tmp;
}
正确写法
for ( int & i= cur[ x] ; i&& res; i= nx[ i] ) if ( ec[ i] > 0 && dis[ x] + 1 == dis[ e[ i] ] ) {
ll tmp= dg ( e[ i] , min ( res, ec[ i] ) ) ;
ec[ i] - = tmp, ec[ i^ 1 ] + = tmp, res- = tmp;
if ( ! res) break ;
}
因为可能res为0的时候退出for循环还会执行跳当前弧的操作,但实际上这个当前弧还没有流满,所以要在for循环之前退出。
#include <cstdio>
#include <cmath>
#include <cstring>
#include <algorithm>
#define maxn 505
#define maxm 20005
#define inf 1e13
#define ll long long
using namespace std;
int Tt, n, w, p, q, i, j, k, S, T, tot, tp[ maxn] ;
int em, e[ maxm] , nx[ maxm] , ls[ maxn] ; ll ec[ maxm] , s[ maxn] , ans;
void insert ( int x, int y, ll z) {
em++ , e[ em] = y, nx[ em] = ls[ x] , ls[ x] = em, ec[ em] = z;
em++ , e[ em] = x, nx[ em] = ls[ y] , ls[ y] = em, ec[ em] = 0 ;
}
void read ( int & x) {
x= 0 ; char ch= getchar ( ) ;
for ( ; ch< '0' || ch> '9' ; ch= getchar ( ) ) ;
for ( ; ch>= '0' && ch<= '9' ; ch= getchar ( ) ) x= x* 10 + ch- '0' ;
}
void add ( int x, int y, int v, int d) {
insert ( x, y, v* 2 ) , insert ( y, x, v* 2 ) ;
s[ x] + = d, s[ y] - = d;
}
int d[ maxn] , dis[ maxn] , cur[ maxn] ;
int bfs ( ) {
memset ( dis, 0 , sizeof ( dis) ) ;
int t= 0 , w= 1 ; d[ 1 ] = S, dis[ S] = 1 ;
while ( t< w) {
int x= d[ ++ t] ; cur[ x] = ls[ x] ;
for ( int i= ls[ x] ; i; i= nx[ i] ) if ( ec[ i] && ! dis[ e[ i] ] )
dis[ e[ i] ] = dis[ x] + 1 , d[ ++ w] = e[ i] ;
}
return dis[ T] > 0 ;
}
ll dg ( int x, ll p) {
if ( x== T) return p;
ll res= p;
for ( int & i= cur[ x] ; i&& res; i= nx[ i] ) if ( ec[ i] > 0 && dis[ x] + 1 == dis[ e[ i] ] ) {
ll tmp= dg ( e[ i] , min ( res, ec[ i] ) ) ;
ec[ i] - = tmp, ec[ i^ 1 ] + = tmp, res- = tmp;
if ( ! res) break ;
}
return p- res;
}
ll maxflow ( ) {
ll sum= 0 ;
while ( bfs ( ) )
sum+ = dg ( S, inf) ;
return sum+ ans;
}
int main ( ) {
read ( Tt) ;
while ( Tt-- ) {
read ( n) , read ( w) , read ( p) , read ( q) ;
memset ( tp, 0 , sizeof ( tp) ) ;
memset ( s, 0 , sizeof ( s) ) ;
memset ( ls, 0 , sizeof ( ls) ) , em= 1 ;
S= n+ 1 , T= n+ 2 , ans= 0 ;
for ( i= 1 ; i<= p; i++ ) {
int x, y, z, a, b, c, d, e, f;
read ( x) , read ( y) , read ( z) , read ( a) , read ( b) , read ( c) , read ( d) , read ( e) , read ( f) ;
add ( x, y, a, d) , add ( y, z, b, e) , add ( z, x, c, f) ;
}
for ( i= 1 ; i<= q; i++ ) {
int x, y, r;
read ( x) , read ( y) , read ( r) ;
if ( r== 0 ) insert ( y, x, inf) ;
if ( r== 1 ) insert ( x, y, inf) , insert ( y, x, inf) ;
if ( r== 2 ) tp[ y] = 1 , tp[ x] = - 1 ;
}
for ( i= 1 ; i<= n; i++ ) {
s[ i] ++ , ans- = abs ( s[ i] ) ;
insert ( S, i, ( tp[ i] == - 1 ) ? inf: ( abs ( s[ i] ) + s[ i] ) ) ;
insert ( i, T, ( tp[ i] == 1 ) ? inf: ( abs ( s[ i] ) - s[ i] ) ) ;
}
printf ( "%lld\n" , maxflow ( ) * w) ;
}
}