• 「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

    对于任意的 (1le i<m,1le jle n),一定满足:

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

    对应地,对于任意的 (1le ile n,1le 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)) 就一定会被翻转到;此外,由于 (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))


    另一种看法:用矩阵操作构造出“方形”、“分隔横行”、“分割纵列”、“四角散点”四种操作,这样所有的方形都可以利用横纵操作消除,最终只留下一个方形,可以利用横纵操作将它移到左上角;相似地,横行纵列可以利用散点来平移,从而彼此消除,最终在所在行、列上只留下一个位于边界的操作,接着就可以枚举情况了。

    虽然我不会写,但是这个方法听起来很有意思。

    小结:

    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( '
    ' );
        return 0;
    }
    
  • 相关阅读:
    晶振故障原因
    国外被疯赞的一篇神文:你该增加人生技能了(转)
    Python学习笔记-Day27-正则表达式
    Python学习笔记-Day25-模块(hashlib、configpaser,logging)
    Python学习笔记-Day24-collections模块
    Python学习笔记-Day23-模块(时间、随机数、sys、os)
    Python学习笔记-Day22-内置方法及序列化模块
    Python学习笔记-Day21-反射、__str__、__repr__
    @classmethod 与 @staticmethod
    @property(setter、deleter)将类中的方法伪装成属性
  • 原文地址:https://www.cnblogs.com/crashed/p/15421909.html
Copyright © 2020-2023  润新知