P10743 [SEERC2020] AND = OR 题解
非常巧妙的一个题。我们首先考虑单组询问该怎么做。
首先需要注意到一个结论,即设答案为 \(x\),那么对于 \(\forall y < x\),\(y\) 都应该放在与组;同样的,对于 \(\forall y > x\),\(y\) 都应该放在与组。
进一步的,我们观察在 \(\text{popcount}\) 上也有同样的性质,即对于 \(\forall y, \text{popcount(y)} < \text{popcount(x)}\),\(y\) 应该被放到或组,反之同理。
而对于 \(\text{popcount(y) = \text{popcount(x)}}\) 的 \(y\),有结论:所有的 \(y\) 相等。
证明非常的巧妙,设或集合为 \(A\),与集合为 \(B\),\(\text{popcount(x)} = k\)。
当只有一个数时显然成立,现在考虑大于一个数的情况。
从这些数中拿出两个,分别设为 \(a, b\),并分别放入 \(A, B\) 中,设 \(A, B\) 内的数经运算后的值分别为 \(v1, v2\)。
显然有 \(\text{popcount}(v1) \ge k, \text{popcount}(v2) \le k\),又因为两个集合的答案相等,所以 \(\text{popcount}(v1) = \text{popcount}(v2) = k\)。
考虑按位与或的本质,是把二进制下的位置分别取并和交,因为两者都恰好有 \(k\) 位,所以 \(v1 = a, v2 = b\),那么进一步就有 \(a = b\)。
大于 \(2\) 个数字都可以用类似的证明。
考虑答案怎么维护,考虑枚举答案的 \(\text{popcount} = k\),依次 check 每个区间是否合法,维护 \(\text{popcount} < k\) 的 \(A\) 集合值和 \(\text{popcount} > k\) 的 \(B\) 集合值,可以用线段树和前缀和在 \(\log^2\) 内完成。
细节上对于 \(\text{popcount} = k\) 的数,要分别对数量为 \(1, 2, \ge 2\) 分别讨论。
// ubsan: undefined
// accoders
// 如果命运对你缄默, 那就活给他看。
#pragma GCC optimize(1)
#pragma GCC optimize(2)
#pragma GCC optimize(3)
#pragma GCC optimize("Ofast", "inline", "-ffast-math")
#pragma GCC target("avx,sse2,sse3,sse4,mmx")
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
// #define int LL
const int maxn = 100010;
const int INF = (1 << 30) - 1;
int n, q, w[maxn], pc[maxn];
namespace sgt {
struct Node { int o, a, c, v; };
struct sgt {
Node t[maxn << 2];
inline Node mg(Node ls, Node rs) {
return {ls.o | rs.o, ls.a & rs.a, ls.c + rs.c,
(ls.v == -1 || rs.v == -1) ? max(ls.v, rs.v) : ((ls.v ^ rs.v) ? INF + 1 : rs.v)
};
}
inline void build(int u, int l, int r, int c) {
if(l == r) {
if(pc[r] == c) t[u] = {w[r], w[r], 1, w[r]};
else t[u] = {0, INF, 0, -1};
return ;
}
int mid = l + r >> 1;
build(u << 1, l, mid, c);
build(u << 1 | 1, mid + 1, r, c);
t[u] = mg(t[u << 1], t[u << 1 | 1]);
}
inline Node Q(int u, int l, int r, int ql, int qr) {
if(ql <= l && r <= qr) return t[u];
int mid = l + r >> 1;
if(qr <= mid) return Q(u << 1, l, mid, ql, qr);
if(ql > mid) return Q(u << 1 | 1, mid + 1, r, ql, qr);
return mg(Q(u << 1, l, mid, ql, qr), Q(u << 1 | 1, mid + 1, r, ql, qr));
}
} t[31];
} using namespace sgt;
int a[31], o[31];
inline bool check(int l, int r) {
for(int i = 0; i < 31; ++ i) {
Node e = t[i].Q(1, 1, n, l, r);
a[i] = e.a, o[i] = e.o;
}
for(int i = 1; i < 31; ++ i) o[i] |= o[i - 1];
for(int i = 29; ~i; -- i) a[i] &= a[i + 1];
int co = 0, ca = r - l + 1;
for(int k = 0; k < 31; ++ k) {
Node e = t[k].Q(1, 1, n, l, r);
ca -= e.c;
int vo = (!k) ? 0 : o[k - 1], va = (k == 30 ? INF : a[k + 1]);
if(e.v ^ (INF + 1)) {
int cnt = e.c;
if((cnt == 0) && (ca && co && (vo == va))) return 1;
if((cnt == 1) && ((((vo | e.v) == va) && ca) || ((vo == (e.v & va)) && co))) return 1;
if((cnt >= 2) && ((vo | e.v) == (va & e.v))) return 1;
}
co += e.c;
}
return 0;
}
signed main() {
// freopen("peace.in", "r", stdin);
// freopen("peace.out", "w", stdout);
ios :: sync_with_stdio(false);
cin.tie(0), cout.tie(0);
cin >> n >> q;
for(int i = 1; i <= n; ++ i) {
cin >> w[i];
pc[i] = __builtin_popcount(w[i]);
}
for(int i = 0; i < 31; ++ i) t[i].build(1, 1, n, i);
for(int l, r, i = 1; q --; ) {
cin >> l >> r;
cout << (check(l, r) ? "YES" : "NO") << '\n';
}
return 0;
}