新生赛题解(最大的最短或子段)
\(题解:\)
\(我们考虑到其最大值是固定的\)
\(所以我们优先求出其最大值\)
\(考虑到其答案肯定是一个固定的区间\)
\(对于每个区间 如果将其范围缩小 它的取值或值只会单调不增\)
\(那么对于每个区间 首先选定其左端点 然后我们向右不断拓展 区间长度\)
\(一直到其区间或值为最大值\)
\(但是我们怎么维护区间值的增删呢\)
\(考虑给最大值的二进制的每一位进行标记(这是算法竞赛中很重要的一种思想拆位)\)
\(建议各位新生以后在遇到 区间& | ^问题时首先思考能否进行拆位 进行加速\)
\(因为最大值 很明显是固定的\)
\(所以我们维护其区间二进制每一位出现的次数\)
\(如果最大值上存在的一位的二进制位消失不见 那么这个区间不是最大区间 考虑继续拓展区间长度\)
\(综合复杂度为O(nlogn)\)
\(在出数据时考虑到新生很难想到拆位思想 所以放了一些优化暴力都可通过的数据(并未太过毒瘤)\)
点击查看代码
#include <bits/stdc++.h>
using namespace std;
//---------------------------//
#ifdef LOCAL
template <class... Args> void debug(const Args&... args) { ((cerr << args << ", "), ...) << '\n'; }
#define debug(args...) cerr << #args << ": ", debug(args)
#define debugsq(x) cerr << #x << ": ["; for (auto i : x) cerr << i << ' '; cerr << "]\n";
#define debugmp(x) cerr << #x << ": [ "; for (auto [i, j] : x) cerr << '[' << i << "," << j << "] "; cerr << "]\n";
#else
#define debug(...) ;
#define debugsq(...) ;
#define debugmp(...) ;
#endif
//---------------------------//
// 核心解法函数
void solve() {
int n;
cin >> n;
vector<int> a(n + 2, 0), cnt(32, 0); // a 用于存储输入数组,cnt 用于记录每个二进制位的出现次数
int mx = 0, cnt_gg = 0; // mx 存储整个数组的最大或值,cnt_gg 记录需要满足的二进制位数
// 计算数组的整体最大或值
for (int i = 1; i <= n; i++) {
cin >> a[i];
mx |= a[i];
}
// 找出 mx 的所有二进制位中为 1 的位数,并初始化 cnt 数组和 cnt_gg 计数器
for (int i = 0; i < 32; i++) {
if (mx >> i & 1) {
cnt[i] = 1;
cnt_gg++;
}
}
// debugsq(cnt); // 调试:输出 cnt 数组的状态
fill(cnt.begin(), cnt.end(), 0); // 重置 cnt 数组为 0
int l = n, r = n, ans = n; // 双指针窗口的左右端点初始化为数组末尾,ans 初始化为数组长度
// 反向滑动窗口,寻找最短子段
while (l > 0 && r > 0) {
// 减去 r+1 元素的二进制位贡献
for (int i = 0; i < 32; ++i) {
if ((a[r + 1] >> i) & 1) { // 若 a[r+1] 的第 i 位为 1
if (cnt[i] == 1) cnt_gg++; // 若该位在 cnt 数组中的计数为 1,cnt_gg 增加
cnt[i]--; // 将该位计数减 1
}
}
// 调整左边界 l
if (l > r) l = r;
// 将 l 左移,直到窗口中的或值覆盖 mx 的所有 1 位
while (cnt_gg > 0 && l > 0) {
for (int i = 0; i < 32; ++i) {
if ((a[l] >> i) & 1) { // 若 a[l] 的第 i 位为 1
if (cnt[i] == 0) cnt_gg--; // 若该位原本在 cnt 中为 0,则 cnt_gg 减少
cnt[i]++; // 增加该位计数
}
}
--l; // 移动左端点
}
// 若此时 cnt_gg 为 0,表示当前窗口包含了 mx 所有的 1 位
if (cnt_gg == 0) {
ans = min(ans, r - l); // 更新最短长度
}
--r; // 缩小右边界
}
cout << ans << endl; // 输出结果
}
signed main() {
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
int _ = 1;
// cin >> _; // 若有多组测试数据,取消注释此行
while (_--) {
solve();
}
}

浙公网安备 33010602011771号