• 「Codechef」Xor Matrix


    题目

    点这里看题目。

    分析

    Note.

    重要提示:不会做,就打表,找规律

    注意到 \([L,R]\) 实际上只限定了范围,如果有 \([L_1,R_1]\supseteq [L_2,R_2]\),则 \([L_2,R_2]\) 对应的矩阵一定是 \([L_1,R_1]\) 对应的矩阵的子矩阵。

    也即,如果我们将所有非负整数按照这种方式画出一个无穷的矩阵,我们的 \([L,R]\) 实际上就是从中截取一部分后得到的矩阵

    考察这个矩阵的全局性质:套路地讨论倍增的性质。设 \(A_k\) 表示 \([0,2^k-1]\) 给出的矩阵,我们可以得到:

    \[A_k=\begin{bmatrix} A_{k-1}&A_{k-1}\oplus 2^k\\ A_{k-1}\oplus 2^k&A_{k-1} \end{bmatrix} \]

    其中 \(A_k\oplus b\) 表示对于矩阵中每一个元素都异或上 \(b\)。边界情况是 \(A_0=[0]\)。这样一条性质指出,\(A_k\) 上的最长路径结尾必然是 \(2^k-1\)

    此外,在 \(A_k\) 里面,从 \(0\) 到右上角的合法路径条数为 \(2^k\),所有最大化结尾值的路径条数为 \(2^{2k}\)。归纳证明即可。

    另一个套路的想法是,我们\([L,R]\) 拆分成若干个 \([p,p+2^k-1]\) 形式的 01-Trie 区间。每个区间内的数由于高位全部相同,其实就等价于一个 \(A_k\)。此时独立看待这个矩阵就可以生成 \(2^{2k}\) 条结尾为 \(2^k-1\) 的最大路径。

    还有交叉的情况!比如说,我们可以先走到 \(A_k\) 的右上角,再向上走出这个矩阵。考虑:

    032
    301
    210
    

    这里我们就可以走出 0123 的路径。

    为了更好地讨论,我们假装将这个 \(A_k\) 放在了一个 \(A_{k+1}\) 里面。如果这里对应的是右下角的那个 \(A_k\)(在 01-Trie 上,该区间是一个右儿子),那么任何路径都不可能向下或者向右拓展,否则就会出现较高位上出现一个 \(1\) 导致不连续。

    如果向上拓展的话,根据构造过程,\(A_k\oplus 2^{k+1}\) 只需要包含至少一行即可将原先的 \(2^k-1\) 的路径拓展到 \(2^{k+1}-1\)。并且,由于 01-Trie 拆分的区间是极长的,上面的 \(A_k\oplus 2^{k+1}\) 不可能被完全包含,因而我们也无法作进一步的拓展。

    Remark.

    这个性质不是很容易看出来。但是还是那句话,搞不懂,就找个例子出来看一看,找规律

    计算方案数也比较容易:我们注意到,从任何一个副对角线上的 \(2^{k+1}-1\) 走到右下角都有且仅有一条路径,所以我们只需要数一下有多少个 \(2^{k+1}-1\) 即可。在 01-Trie 拆分的背景下这是极其容易的。

    当然,这里对应左上角的那个 \(A_k\) 的情况类似。最后我们需要将多个区间的结果合并,这也很容易,比一下大小,再将最值对应的路径条数累加即可。

    最终复杂度可以做到 \(O(T\log R)\)

    代码

    #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 int mod = 1e9 + 7;
    
    template<typename _T>
    inline 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>
    inline void Write( _T x ) {
    	if( x < 0 ) putchar( '-' ), x = -x;
    	if( 9 < x ) Write( x / 10 );
    	putchar( x % 10 + '0' );
    }
    
    template<typename _T>
    inline _T Max( const _T &a, const _T &b ) {
    	return a > b ? a : b;
    }
    
    LL L, R;
    LL ans = 0; int ways = 0;
    
    int T;
    
    inline int Mul( int x, const int &v ) { return 1ll * x * v % mod; }
    inline int Sub( int x, const int &v ) { return ( x -= v ) < 0 ? x + mod : x; }
    inline int Add( int x, const int &v ) { return ( x += v ) >= mod ? x - mod : x; }
    
    inline int& MulEq( int &x, const int &v ) { return x = 1ll * x * v % mod; }
    inline int& SubEq( int &x, const int &v ) { return ( x -= v ) < 0 ? ( x += mod ) : x; }
    inline int& AddEq( int &x, const int &v ) { return ( x += v ) >= mod ? ( x -= mod ) : x; }
    
    inline int Qkpow( int base, int indx ) {
    	int ret = 1;
    	while( indx ) {
    		if( indx & 1 ) MulEq( ret, base );
    		MulEq( base, base ), indx >>= 1;
    	}
    	return ret;
    }
    
    inline LL Lowbit( const LL &x ) {
    	return x & ( -x );
    }
    
    void Divide( const LL &l, const LL &r, const LL &segL, const LL &segR, const int &side ) {
    	if( segL <= l && r <= segR ) {
    		int k = __builtin_ctzll( r - l + 1 );
    		int w = Qkpow( 2, 2 * k );
    		if( r - l > ans ) ans = r - l, ways = 0;
    		if( r - l == ans ) AddEq( ways, w );
    		if( side == 0 ) {
    			if( r + 1 <= R ) {
    				w = Mul( 2, Mul( Qkpow( 2, k ), ( R - r ) % mod ) );
    				if( ( r - l ) * 2 + 1 > ans ) ans = ( r - l ) * 2 + 1, ways = 0;
    				if( ( r - l ) * 2 + 1 == ans ) AddEq( ways, w );
    			}
    		} else {
    			if( L <= l - 1 ) {
    				w = Mul( 2, Mul( Qkpow( 2, k ), ( l - L ) % mod ) );
    				if( ( r - l ) * 2 + 1 > ans ) ans = ( r - l ) * 2 + 1, ways = 0;
    				if( ( r - l ) * 2 + 1 == ans ) AddEq( ways, w );
    			}
    		}
    		return ;
    	}
    	LL mid = ( l + r ) >> 1;
    	if( segL <= mid ) Divide( l, mid, segL, segR, 0 );
    	if( mid  < segR ) Divide( mid + 1, r, segL, segR, 1 );
    }
    
    int main() {
    	Read( T );
    	while( T -- ) {
    		ans = 0, ways = 0;
    		Read( L ), Read( R );
    		Divide( 0, ( 1ll << 60 ) - 1, L, R, 0 );
    		Write( ans ), putchar( ' ' );
    		Write( ways ), putchar( '\n' );
    	}
    	return 0;
    }
    
  • 相关阅读:
    POJ1321 棋盘问题
    HDU1234 开门人和关门人(解法二)
    HDU1234 开门人和关门人(解法二)
    HDU1996 汉诺塔VI
    HDU1996 汉诺塔VI
    HDU1013 POJ1519 Digital Roots(解法二)
    HDU1013 POJ1519 Digital Roots(解法二)
    HDU4548 美素数
    HDU4548 美素数
    POJ3751 时间日期格式转换【日期计算】
  • 原文地址:https://www.cnblogs.com/crashed/p/16529981.html
Copyright © 2020-2023  润新知