题目
点这里看题目。
分析
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\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;
}