「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;
}
posted @ 2022-07-28 19:49  crashed  阅读(87)  评论(1编辑  收藏  举报