zoukankan      html  css  js  c++  java
  • [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;
    }
    
  • 相关阅读:
    正则表达式解决身份证号码和手机号
    redis:集群配置
    linux:NFS
    xshell提示必须安装最新的更新
    linux:ssh远程调用tomcat脚本时候出错
    linux:scp从入门到刚入门
    linux:SSH最简单教程
    nginx;keepalived配置出现主主的解决方法(脑裂问题)
    (4)事件处理——(4)网页上的多个脚本(Multiple scripts on one page)
    [php]应用控制器(一)
  • 原文地址:https://www.cnblogs.com/crashed/p/14170437.html
Copyright © 2011-2022 走看看