1. 程式人生 > 其它 >「CF321D」Ciel and Flipboard

「CF321D」Ciel and Flipboard

題目

點這裡看題目。

分析

實在是一道巧妙的打表找規律分析題目!

不難想到每種翻轉方案只需要最多執行一次。那麼可以設 \(s_{i,j}\) 表示最終 \((i,j)\) 這個位置的值為 \((-1)^{s_{i,j}}a_{i,j}\)

接下來,這道題可以分析出兩個重要結論,但是考慮到 They're much easier to be proven than found,所以我也只會證明

結論 1

對於任意的 \(1\le i<m,1\le j\le n\),一定滿足:

\[s_{i,j}\oplus s_{m,j}\oplus s_{i+m,j}=0 \]

對應地,對於任意的 \(1\le i\le n,1\le j<m\)

,一定滿足:

\[s_{i,j}\oplus s_{i,m}\oplus s_{i,j+m}=0 \]

如何發現這個結論?打表,但也不見得會變得簡單

如何證明這個結論?“很簡單”。以第一部分為例,注意到 \([i,i+m]\) 之中一共包含 \(m+1\) 個格子,並且 \(m=\frac{n+1}{2}\),所以不管怎樣操作,只要有一次翻轉到了這一列,那麼 \((m,j)\) 就一定會被翻轉到;此外,由於 \(1\le 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\) 其實坐落在中心十字上。如果我們列舉所有 \(1\le j\le m\)\(s_{m,j}\),那麼剩下的 \(s\) 只會在行上相互影響,也就是行與行獨立。我們只需要對於每一行,列舉中間位置的 \(s\),然後貪心地計算該行以及受該行影響的位置的結果即可。

時間複雜度為 \(O(2^m\times m^2)\)


另一種看法:用矩陣操作構造出“方形”、“分隔橫行”、“分割縱列”、“四角散點”四種操作,這樣所有的方形都可以利用橫縱操作消除,最終只留下一個方形,可以利用橫縱操作將它移到左上角;相似地,橫行縱列可以利用散點來平移,從而彼此消除,最終在所在行、列上只留下一個位於邊界的操作,接著就可以列舉情況了。

雖然我不會寫,但是這個方法聽起來很有意思。

小結:

  1. 一定要加強尋找結論的意識,如果覺得思考起來有困難、暴力不復雜、狀態不過分多就可以嘗試打表找規律
  2. 證明的思路都挺有意思的,尤其是藉助方案數來推出充分的結論 2。
  3. 另一種看法裡面的歸約思想要掌握。

程式碼

#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( '\n' );
    return 0;
}