门牌号码查询
牛客 E 题:门牌号码查询
(格雷码)
一、问题重述
给定 ( k ),共有 ( 2^k ) 个位置,位置编号 ( x ) 从 1 到 ( 2^k )。第 ( x ) 个位置的门牌号 ( c_x ) 按如下规则生成:
- 令 ( n = x - 1 )
- 计算中间数:( \text{mid} = n \oplus \lfloor n/2 \rfloor )
- 将
mid写成 ( k ) 位二进制(不足补前导 0),然后完全反转位序,得到十进制数 ( c_x )
有 ( q ) 次询问,每次给出 ( (l, r, h, z) ),统计区间 ( [l, r] ) 内有多少个位置 ( x ) 满足:
[
c_x \bmod 2^h = z
]
二、核心观察:格雷码登场
公式 ( n \oplus (n \gg 1) ) 是经典的二进制反射格雷码定义:
[
G(n) = n \oplus (n \gg 1)
]
所以:
[
c_x = \text{rev}_k(G(n))
]
其中 ( \text{rev}_k ) 表示将 ( k ) 位二进制完全反转(MSB ↔ LSB)。
三、化简条件 ( c_x \bmod 2^h = z )
3.1 用二进制位展开
设 ( n ) 的 ( k ) 位二进制表示为:
[
n = \overline{n_{k-1} n_{k-2} \cdots n_1 n_0}_{(2)}
]
其中 ( n_{k-1} ) 是最高位(MSB),( n_0 ) 是最低位(LSB)。
格雷码 ( G(n) ) 的第 ( i ) 位(从 0 开始)为:
[
g_i = n_i \oplus n_{i+1}
]
特别地,最高位 ( g_{k-1} = n_{k-1} \oplus 0 = n_{k-1} )。
3.2 取模 ( 2^h ) 的含义
( c_x \bmod 2^h ) 取的是 ( c_x ) 的低 ( h ) 位(第 0 到第 ( h-1 ) 位)。
由于 ( c_x ) 是 ( G(n) ) 的位反转,所以:
- ( c_x ) 的第 0 位(LSB)= ( G(n) ) 的第 ( k-1 ) 位 = ( n_{k-1} )
- ( c_x ) 的第 1 位 = ( G(n) ) 的第 ( k-2 ) 位 = ( n_{k-2} \oplus n_{k-1} )
- ( c_x ) 的第 2 位 = ( G(n) ) 的第 ( k-3 ) 位 = ( n_{k-3} \oplus n_{k-2} )
- (\cdots)
- ( c_x ) 的第 ( h-1 ) 位 = ( G(n) ) 的第 ( k-h ) 位 = ( n_{k-h} \oplus n_{k-h+1} )
如果 ( z ) 的二进制表示为:
[
z = \overline{z_{h-1} z_{h-2} \cdots z_1 z_0}_{(2)}
]
(( z_0 ) 为 LSB),则条件 ( c_x \bmod 2^h = z ) 等价于:
[
\begin{cases}
z_0 = n_{k-1} \
z_1 = n_{k-2} \oplus n_{k-1} \
z_2 = n_{k-3} \oplus n_{k-2} \
\cdots \
z_{h-1} = n_{k-h} \oplus n_{k-h+1}
\end{cases}
]
四、引入前缀 ( p )
定义:
[
p = \left\lfloor \frac{n}{2^{k-h}} \right\rfloor
]
即取 ( n ) 的最高 ( h ) 位。设 ( p ) 的二进制表示为:
[
p = \overline{p_{h-1} p_{h-2} \cdots p_1 p_0}_{(2)}
]
则有对应关系:
[
\begin{cases}
p_{h-1} = n_{k-1} \
p_{h-2} = n_{k-2} \
\cdots \
p_0 = n_{k-h}
\end{cases}
]
代入第 3 节的方程组:
[
\begin{cases}
z_0 = p_{h-1} \
z_1 = p_{h-2} \oplus p_{h-1} \
z_2 = p_{h-3} \oplus p_{h-2} \
\cdots \
z_{h-1} = p_0 \oplus p_1
\end{cases}
]
五、揭示本质:格雷码的双射关系
定义 ( z^R ) 为 ( z ) 的位反转(( h ) 位):
[
z^R = \text{rev}_h(z)
]
那么 ( z^R ) 的二进制位从高到低为 ( z_{h-1}, z_{h-2}, \dots, z_0 )。
把第 4 节的式子反过来看,从高到低排列:
[
\begin{cases}
z_{h-1} = p_0 \oplus p_1 \
z_{h-2} = p_1 \oplus p_2 \
\cdots \
z_0 = p_{h-1} \oplus 0
\end{cases}
]
这正是二进制数 ( p ) 到格雷码 ( z^R ) 的转换公式:
[
\boxed{z^R = G(p)}
]
因为格雷码是 ( h ) 位空间上的双射(一一对应),所以:
关键结论: 对于任意给定的 ( h ) 和 ( z ),存在唯一一个 ( p )(( 0 \le p < 2^h ))满足 ( G(p) = \text{rev}_h(z) )。
六、求唯一 ( p ):格雷码逆变换
已知 ( z ),求 ( p ) 的步骤:
6.1 反转 ( z ) 的位
[
z^R = \text{rev}_h(z)
]
6.2 格雷码逆变换(从高位到低位递推)
令 ( z^R ) 的位从高到低为 ( z^R_{h-1}, z^R_{h-2}, \dots, z^R_0 ),对应的 ( p ) 位从高到低为 ( p_{h-1}, p_{h-2}, \dots, p_0 )。
格雷码定义:( z^R_i = p_i \oplus p_{i+1} ),其中定义 ( p_h = 0 )。
逆推:
- 最高位:( p_{h-1} = z^R_{h-1} )
- 然后依次:( p_{h-2} = z^R_{h-2} \oplus p_{h-1} )
- ( p_{h-3} = z^R_{h-3} \oplus p_{h-2} )
- (\cdots)
- ( p_0 = z^R_0 \oplus p_1 )
七、由 ( p ) 反推 ( x ) 的区间
根据 ( p ) 的定义:
[
p = \left\lfloor \frac{n}{2^{k-h}} \right\rfloor
]
这等价于:
[
p \cdot 2^{k-h} \le n \le (p+1) \cdot 2^{k-h} - 1
]
由于 ( n = x - 1 ),可得:
[
p \cdot 2^{k-h} + 1 \le x \le (p+1) \cdot 2^{k-h}
]
记:
[
L = p \cdot 2^{k-h} + 1,\quad R = (p+1) \cdot 2^{k-h}
]
因此,所有满足条件的 ( x ) 恰好构成一个连续整数区间 ([L, R])。
八、查询转化为区间交集
对于每次询问 ((l, r, h, z)),处理流程如下:
-
特殊情况 ( h = 0 ):
因为 ( 2^0 = 1 ),任意整数模 1 的余数均为 0,所以区间内全部满足。
[
\text{答案} = r - l + 1
] -
一般情况 ( h > 0 ):
- 将 ( z ) 的 ( h ) 个二进制位完全反转,得到 ( z^R )。
- 对 ( z^R ) 执行格雷码逆变换,得到唯一的 ( p )(逆变换方法见第六节)。
- 计算目标区间 ([L, R])。
- 最终答案即为查询区间 ([l, r]) 与目标区间 ([L, R]) 的交集长度:
[
\text{ans} = \max\bigl(0,; \min(r, R) - \max(l, L) + 1\bigr)
]
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
// 将长度为 bits 的格雷码 g 转换为二进制数 p(逆变换)
ll gray_to_binary(ll g, int bits) {
ll p = 0;
ll prev = 0; // 更高一位的二进制值(最高位前视为 0)
for(int i = bits - 1; i >= 0; i --) {
int gi = (g >> i) & 1;
int bi = gi ^ prev; // 因为 gi = bi ^ prev,所以 bi = gi ^ prev
if(bi) p |= (1LL << i);
prev = bi;
}
return p;
}
// 将长度为 bits 的整数 x 的二进制位完全反转
ll reverse_bits(ll x, int bits) {
ll res = 0;
for(int i = 0; i < bits; i ++)
if(x & (1LL << i)) res |= (1LL << (bits - 1 - i));
return res;
}
void solve() {
int k, q; cin >> k >> q;
while(q --) {
ll l, r, h, z; cin >> l >> r >> h >> z;
// h = 0 时,任何数模 1 余 0,区间内全部满足
if(h == 0) {
cout << r - l + 1 << '\n';
continue;
}
// 1. 将 z 的 h 个二进制位反转得到 z_rev
ll z_rev = reverse_bits(z, (int)h);
// 2. 由 z_rev 恢复出唯一的前缀 p(格雷码逆变换)
ll p = gray_to_binary(z_rev, (int)h);
// 3. 计算目标 x 区间
ll block = 1LL << (k - (int)h); // 2^(k-h)
ll L = p * block + 1;
ll R = (p + 1) * block;
// 4. 与查询区间 [l, r] 取交集
ll left = max(l, L);
ll right = min(r, R);
if(left > right) cout << 0 << '\n';
else cout << right - left + 1 << '\n';
}
}
int main() {
ios::sync_with_stdio(false); cin.tie(0);
int t = 1; //cin >> t;
while(t --) solve();
return 0;
}

浙公网安备 33010602011771号