hdu1001 TLE部分思路
优雅集合与最小答案求解
题意回顾
给定一个优雅集合 $S$ 满足:
- $0,, 2^{64}-1 \in S$;
- 对任意 $u,v\in S$,有 $u&v\in S$ 且 $u,|,v\in S$。
现在已知 $S$ 中的 $n$ 个元素(记为集合 $A$),对于每个查询给定一个自然数 $x$,要求求出最小的自然数 $y\ge x$,使得 $y$ 一定在 $S$ 中。
思路说明
我们可以证明,对于任意优雅集合 $S$,由已知集合 $A$ 可以推出每一位上的“必然取值”。具体做法如下:
-
预处理“必然值”
对于每个二进制位 $b$($0\le b<64$),令
\[\texttt{bit}[b] = \bigwedge\{\,a : a\in A \text{ 且 }a\text{的第 }b\text{位为 }1\}, \]如果没有已知数在该位为 $1$,则令 $\texttt{bit}[b]=2^{64}-1$。
可以证明:对于任意 $y\in S$,如果 $y$ 的第 $b$ 位为 $1$,则必须有
\[y\&\texttt{bit}[b]=\texttt{bit}[b]. \] -
问题转化
对于任意查询给定 $x$,问题转化为求满足下列条件的最小 $y$:
- $y\ge x$;
- 对于所有 $0\le b<64$,若 $y$ 的第 $b$ 位为 $1$,则有\[y\&\texttt{bit}[b]=\texttt{bit}[b]. \]
在满足上述条件的 $y$ 中取最小值。
-
构造候选答案
如果 $x$ 本身满足条件,则答案即为 $x$。否则,我们枚举 $i=0,1,\dots,63$ 构造候选答案:
\[y_i=\left(\left\lfloor\frac{x}{2^{i+1}}\right\rfloor\times2^{i+1}\right)\,|\,\texttt{bit}[i], \]即保留 $x$ 高于低 $i+1$ 位的部分,然后低位统一填上由 $\texttt{bit}[i]$ 给出的模式。
在所有满足 $y_i\ge x$ 且满足条件的候选中,取最小者。
完整 C++ 代码
下面给出完整代码,其中用内联函数 valid(y, bitArr) 判断 $y$ 是否满足“必然性条件”,并对每个查询构造候选答案,最后将答案取模后异或输出。
#include <bits/stdc++.h>
using namespace std;
// 模数 M = 10^16 + 1029
const unsigned long long M = 10000000000000000ULL + 1029ULL;
// 检查 y 是否满足:对于每个 b (0<=b<64),若 y 的第 b 位为 1,则必须有 (y & bitArr[b]) == bitArr[b]。
inline bool valid(unsigned long long y, const unsigned long long bitArr[64]) {
for (int b = 0; b < 64; b++) {
if ((y >> b) & 1ULL) {
if ((y & bitArr[b]) != bitArr[b])
return false;
}
}
return true;
}
// 快速读取 unsigned long long(可根据需要调整)
static inline bool scanULL(unsigned long long &x) {
int c = getchar();
if(c == EOF) return false;
while(c != EOF && (c==' ' || c=='\n')) c = getchar();
if(c == EOF) return false;
x = 0;
while(c != EOF && c >= '0' && c <= '9'){
x = x * 10ULL + (unsigned long long)(c - '0');
c = getchar();
}
return true;
}
int main(){
ios::sync_with_stdio(false);
cin.tie(nullptr);
int T;
cin >> T;
while(T--){
int n, q;
cin >> n >> q;
vector<unsigned long long> a(n);
for (int i = 0; i < n; i++){
cin >> a[i];
}
// 预处理 bitArr[0..63],初始化为全 1,即 2^64 - 1
unsigned long long bitArr[64];
for (int b = 0; b < 64; b++){
bitArr[b] = ~0ULL;
}
// 对每个 a,如果 a 的第 b 位为1,则 bitArr[b] &= a
for (int i = 0; i < n; i++){
for (int b = 0; b < 64; b++){
if ((a[i] >> b) & 1ULL)
bitArr[b] &= a[i];
}
}
unsigned long long xorAns = 0;
// 对每个查询,求最小 y >= x 且 y 必定在 S 中
for (int qi = 0; qi < q; qi++){
unsigned long long x;
cin >> x;
unsigned long long best = ~0ULL; // 初始化为 2^64 - 1
// 如果 x 本身满足条件,则候选答案可以为 x
if (valid(x, bitArr))
best = x;
// 枚举 i = 0 .. 63 构造候选答案
for (int i = 0; i < 64; i++){
// 舍去低 i+1 位:右移后再左移
unsigned long long prefix = (x >> (i+1)) << (i+1);
unsigned long long cand = prefix | bitArr[i];
if (cand >= x && valid(cand, bitArr))
best = min(best, cand);
}
// 将答案取模后异或累积
unsigned long long modAns = best % M;
xorAns ^= modAns;
}
cout << xorAns << "\n";
}
return 0;
}
代码说明
-
预处理部分
用长度为 64 的数组
bitArr保存每一位的“必然值”,初始化为全 1(即 $2^{64}-1$)。对于每个已知的 $a\in A$,如果其第 $b$ 位为 1,则更新:\[\texttt{bitArr}[b] \,\&=\, a. \]若某一位从未出现过 1,则保持为全 1。
-
合法性判断
函数
valid(y, bitArr)对 $y$ 的每一位进行检查:若 $y$ 的第 $b$ 位为 1,则必须满足\[y\&\texttt{bitArr}[b]=\texttt{bitArr}[b]. \] -
构造候选答案
对于每个查询给定 $x$,先检查 $x$ 是否满足条件,若满足则答案为 $x$。否则,枚举 $i=0,\dots,63$ 构造候选答案:
\[y_i=\left(\left\lfloor\frac{x}{2^{i+1}}\right\rfloor \times2^{i+1}\right) \,|\, \texttt{bitArr}[i]. \]在所有满足 $y_i\ge x$ 且合法的候选中取最小者。
-
答案输出
每组测试中,将所有查询的答案取模 $M = 10^{16} + 1029$ 后,再将这些模值依次异或输出。

浙公网安备 33010602011771号