题目
点这里看题目。
分析
实在是一道巧妙的打表找规律分析题目!
不难想到每种翻转方案只需要最多执行一次。那么可以设 (s_{i,j}) 表示最终 ((i,j)) 这个位置的值为 ((-1)^{s_{i,j}}a_{i,j})。
接下来,这道题可以分析出两个重要结论,但是考虑到 :They're much easier to be proven than found
,所以我也只会证明
结论 1
对于任意的 (1le i<m,1le jle n),一定满足:
对应地,对于任意的 (1le ile n,1le j<m),一定满足:
如何发现这个结论?打表,但也不见得会变得简单
如何证明这个结论?“很简单”。以第一部分为例,注意到 ([i,i+m]) 之中一共包含 (m+1) 个格子,并且 (m=frac{n+1}{2}),所以不管怎样操作,只要有一次翻转到了这一列,那么 ((m,j)) 就一定会被翻转到;此外,由于 (1le i<m),所以无论怎样操作, ((i,j)) 和 ((i+m,j)) 其中之一一定会被操作到。所以,每次翻转到这一行,这三个数有且只有两个数会被翻转,那么异或和自然为 0。
结论 2
上面的条件显然是一个有效的操作方案的必要条件,而这个结论则说,它也是充分的。
如何发现这个结论?这倒没什么难度,直接从上面开始猜想即可。
如何证明这个结论?上面的结论证明,最终不同的 (s) 的个数 (le 2^{m^2})。而考虑到我们实际上只有 (m^2) 种不同的操作,并且每种操作最多进行一次,如果我们可以说明最终不同的 (s) 的个数 (=2^{m^2}),那么结论 1 必然是充分的。
这其实也不需要什么技巧。假如我们将每种操作看作一个 (n^2) 的向量,那么执行翻转就是对 (s) 异或上向量。考虑 ((1,1)),只有一种操作,不妨称之为 (v_1),会影响到它,那么这一种操作和剩下的 (m^2-1) 种操作一定是异或意义下线性无关的。接着考虑 ((1,2)),只有两种操作,一个是 (v_1)、另一个称之为 (v_2),会影响到它。考虑到线性组合 (v_2) 的时候 (v_1) 的系数一定为 0,所以 (v_2) 和其它操作仍然是线性无关的。一路推理我们可以说明 (m^2) 种操作都是线性无关的。那么最终 (s) 的个数恰好为 (2^{m^2}),因为每种操作执行与否都会真切地影响最终的 (s)。
根据以上的推论,真正需要枚举的 (s) 其实只有 (m^2) 个。更进一步的,最重要的 (s) 其实坐落在中心十字上。如果我们枚举所有 (1le jle m) 的 (s_{m,j}),那么剩下的 (s) 只会在行上相互影响,也就是行与行独立。我们只需要对于每一行,枚举中间位置的 (s),然后贪心地计算该行以及受该行影响的位置的结果即可。
时间复杂度为 (O(2^m imes m^2))。
另一种看法:用矩阵操作构造出“方形”、“分隔横行”、“分割纵列”、“四角散点”四种操作,这样所有的方形都可以利用横纵操作消除,最终只留下一个方形,可以利用横纵操作将它移到左上角;相似地,横行纵列可以利用散点来平移,从而彼此消除,最终在所在行、列上只留下一个位于边界的操作,接着就可以枚举情况了。
虽然我不会写,但是这个方法听起来很有意思。
小结:
- 一定要加强寻找结论的意识,如果觉得思考起来有困难、暴力不复杂、状态不过分多就可以尝试打表找规律。
- 证明的思路都挺有意思的,尤其是借助方案数来推出充分的结论 2。
- 另一种看法里面的归约思想要掌握。
代码
#include <cstdio>
#define rep( i, a, b ) for( int i = (a) ; i <= (b) ; i ++ )
#define per( i, a, b ) for( int i = (a) ; i >= (b) ; i -- )
typedef long long LL;
const LL INF = 1e18;
const int MAXN = 40;
template<typename _T>
void read( _T &x )/*{{{*/
{
x = 0; char s = getchar(); bool f = false;
while( ! ( '0' <= s && s <= '9' ) ) { 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' );
}/*}}}*/
template<typename _T>
_T MAX( const _T a, const _T b )/*{{{*/
{
return a > b ? a : b;
}/*}}}*/
LL A[MAXN][MAXN];
int c[MAXN][MAXN];
int N, M;
#define Val( x, y ) ( c[x][y] ? - A[x][y] : A[x][y] )
int main()
{
read( N ), M = ( N + 1 ) >> 1;
rep( i, 0, N - 1 ) rep( j, 0, N - 1 ) read( A[i][j] );
LL ans = - INF, res, tmp, col, cur; int m = M - 1;
for( int S = 0 ; S < ( 1 << M ) ; S ++ )
{
c[m][m] = S >> ( M - 1 ) & 1;
res = Val( m, m );
for( int i = 0 ; i < m ; i ++ )
{
c[m][i] = S >> i & 1, res += Val( m, i );
c[m][i + M] = c[m][m] ^ c[m][i], res += Val( m, i + M );
}
for( int i = 0 ; i < m ; i ++ )
{
col = - INF;
for( int &d = c[i][m] = 0 ; d < 2 ; d ++ )
{
c[i + M][m] = c[m][m] ^ c[i][m];
tmp = Val( i, m ) + Val( i + M, m );
for( int j = 0 ; j < m ; j ++ )
{
cur = - INF;
for( int &e = c[i][j] = 0 ; e < 2 ; e ++ )
{
c[i][j + M] = c[i][j] ^ c[i][m];
c[i + M][j] = c[i][j] ^ c[m][j];
c[i + M][j + M] = c[i][j + M] ^ c[m][j + M];
cur = MAX( cur, Val( i, j ) + Val( i, j + M ) +
Val( i + M, j ) + Val( i + M, j + M ) );
}
tmp += cur;
}
col = MAX( col, tmp );
}
res += col;
}
ans = MAX( ans, res );
}
write( ans ), putchar( '
' );
return 0;
}