门牌号码查询

牛客 E 题:门牌号码查询

(格雷码)

一、问题重述

给定 ( k ),共有 ( 2^k ) 个位置,位置编号 ( x ) 从 1 到 ( 2^k )。第 ( x ) 个位置的门牌号 ( c_x ) 按如下规则生成:

  1. 令 ( n = x - 1 )
  2. 计算中间数:( \text{mid} = n \oplus \lfloor n/2 \rfloor )
  3. 将 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)),处理流程如下:

  1. 特殊情况 ( h = 0 ):
    因为 ( 2^0 = 1 ),任意整数模 1 的余数均为 0,所以区间内全部满足。
    [
    \text{答案} = r - l + 1
    ]

  2. 一般情况 ( 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;
}
posted @ 2026-10-26 14:17  thenrify  阅读(3)  评论(0)    收藏  举报