[BZOJ]矩阵

题目

点这里看题目。

分析

不难发现:

[|sum_{i=1}^n(A_{ij}-B_{ij})|=|sum_{i=1}^nA_{ij}-sum_{i=1}^nB_{ij}| ]

行求和同理;于是可以发现 (sum A_{ij}) 都是定值,我们只关心 (B) 的行和和列和。

另外一个,原题直接求值显然过于复杂,因而我们可以进行二分,现在我们要判断二分值 (d) 是否合法。

考虑绝对值的含义, (|a-b|le dLeftrightarrow a-dle ble a+d) 。因此我们可以知道 (B) 的每个行和和列和都有自己的范围,现在要做的就是想办法通过 (B_{ij}) 将两种和联系起来。为了方便,我们称 (B) 的第 (i) 行的和为 (R_i) ,第 (j) 列的和为 (C_j)

以下是一种典型的错误想法:对于每个格子建立一个点,并尝试以格子作为左部,行、列作为右部进行建图。

......嗯,这就是错的,因为这会遇到 " 复制 " 流的问题。

不妨这样考虑:我们先给行和确定值,现在只需要将行和拆成 (m) 个元素,并且保证列和合法。

这个思路不难导出如下的做法:

首先建立源汇 (s,t) 。对于每个行建立点,总为左部 (L);对于每个列建立点,总为右部 (R)

然后对于 (uin L) ,连接 (s ightarrow u) ,流量区间为 ([R_i-d,R_i+d]) ;对于 (vin R) ,连接 (v ightarrow t) ,流量区间为 ([C_i-d,C_i+d])

如何表示每一个元素?由于每个元素均可以由一行和一列唯一确定,因此一条边 (u ightarrow v(uin L,vin R)) 就确定了一个元素,那么所有跨部的边的流量区间均为题目给定值。

于是,如果这个图有可行流,就说明 (d) 是合法的。

小结:

通常我们会想到将一个格子抽象为点,而这里是将一个格子抽象为边,从而得到了 行-列 的二分图。

代码

#include <cstdio>

typedef long long LL;

#define int LL

const int INF = 1e8;
const int MAXN = 1e5 + 5, MAXM = 1e6 + 5;
const int MAXS = 205;

template<typename _T>
void read( _T &x )
{
	x = 0; char s = getchar(); int f = 1;
	while( s < '0' || '9' < s ) { f = 1; if( s == '-' ) f = -1; s = getchar(); }
	while( '0' <= s && s <= '9' ) { x = ( x << 3 ) + ( x << 1 ) + ( s - '0' ), s = getchar(); }
	x *= f;
}

template<typename _T>
void write( _T x )
{
	if( x < 0 ) putchar( '-' ), x = -x;
	if( 9 < x ) write( x / 10 );
	putchar( x % 10 + '0' );
}

template<typename _T>
_T MIN( const _T a, const _T b )
{
	return a < b ? a : b;
}

template<typename _T>
_T MAX( const _T a, const _T b )
{
	return a > b ? a : b;
}

struct Edge
{
	int to, nxt, c;
}Graph[MAXM << 1];

int A[MAXN];

int q[MAXN];
int head[MAXN], dep[MAXN], cur[MAXN];
bool vis[MAXN];

int coe[MAXS][MAXS], row[MAXS], col[MAXS];
int N, M, cnt = 1, tot, L, R;

void AddEdge( const int from, const int to, const int C )
{
	Graph[++ cnt].to = to, Graph[cnt].nxt = head[from];
	Graph[cnt].c = C, head[from] = cnt;
}

void AddE( const int from, const int to, const int C ) { AddEdge( from, to, C ), AddEdge( to, from, 0 ); }
void AddELim( const int a, const int b, const int l, const int u ) { AddE( a, b, u - l ), A[a] -= l, A[b] += l; }

bool BFS( const int S, const int T )
{
	int h = 1, t = 0, u, v;
	for( int i = 1 ; i <= tot ; i ++ ) dep[i] = INF;
	dep[q[++ t] = S] = 0;
	while( h <= t )
	{
		u = q[h ++];
		for( int i = head[u] ; i ; i = Graph[i].nxt )
			if( Graph[i].c && dep[v = Graph[i].to] > dep[u] + 1 )
				dep[q[++ t] = v] = dep[u] + 1;
	}
	return dep[T] < INF;
}

int DFS( const int u, const int lin, const int T )
{
	if( u == T ) return lin;
	int used = 0, ret, v, c;
	for( int &i = cur[u] ; i ; i = Graph[i].nxt )
	{
		v = Graph[i].to, c = Graph[i].c;
		if( dep[v] == dep[u] + 1 && c && ( ret = DFS( v, MIN( lin - used, c ), T ) ) )
		{
			used += ret, Graph[i].c -= ret, Graph[i ^ 1].c += ret;
			if( used == lin ) break;
		}
	}
	if( used < lin ) dep[u] = INF;
	return used;
}

int Dinic( const int S, const int T )
{
	int ret = 0;
	while( BFS( S, T ) )
	{
		for( int i = 1 ; i <= tot ; i ++ ) cur[i] = head[i];
		ret += DFS( S, INF, T );
	}
	return ret;
}

void Clean()
{
	cnt = 1, tot = N * M + N + M;
	for( int i = 1 ; i <= tot + 10 ; i ++ )
		head[i] = dep[i] = cur[i] = vis[i] = q[i] = A[i] = 0;
}

bool Chk( const int d )
{
	Clean();
	const int s = ++ tot, t = ++ tot,
			  S = ++ tot, T = ++ tot;
	int pnt = N * M;
	for( int i = 1 ; i <= N ; i ++ ) 
		AddELim( s, i + pnt, row[i] - d, row[i] + d );
	for( int i = 1 ; i <= M ; i ++ ) 
		AddELim( i + pnt + N, t, col[i] - d, col[i] + d );
	for( int i = 1 ; i <= N ; i ++ )
		for( int j = 1 ; j <= M ; j ++ )
			AddELim( i + pnt, j + pnt + N, L, R );
	
	LL ned = 0;
	for( int i = 1 ; i <= tot - 2 ; i ++ )
		if( A[i] > 0 ) AddE( S, i, A[i] ), ned += A[i];
		else if( A[i] < 0 ) AddE( i, T, - A[i] );
	AddE( t, s, INF );
	return Dinic( S, T ) == ned;
}

signed main()
{
	read( N ), read( M );
	for( int i = 1 ; i <= N ; i ++ )
		for( int j = 1 ; j <= M ; j ++ )
			read( coe[i][j] ), row[i] += coe[i][j], col[j] += coe[i][j];
	read( L ), read( R );
	int l = 0, r = INF, mid;
	while( l < r )
	{
		mid = l + r >> 1;
		if( Chk( mid ) ) r = mid;
		else l = mid + 1;
	}
	write( l ), putchar( '
' );
	return 0;
}
原文地址:https://www.cnblogs.com/crashed/p/14170437.html