NFLSOJ #12369 - 「NOIP2021模拟赛0821杭二」序列(猫树分治)
首先考虑什么样的序列可能成为合法的序列。我们考虑枚举小的集合的 OR
及大的集合的 AND
是什么,设为 \(v\),那么显然的性质是,所有 \(<v\) 的数必须丢进 OR 一边的集合,所有 \(>v\) 的数必须丢进 AND 一边的集合,而至于 \(v\),分情况讨论,如果没有值等于 \(v\) 的数,那么显然必须有此时 OR 一边的 bitwise or 要等于 AND 一边的 bitwise and,否则 \(v\) 肯定不可能同时成为小集合的 OR 和大集合的 AND,如果有一个值为 \(v\) 的数,那么这个数最终落在的集合只有两种可能,分别讨论一下即可。如果有两个及以上个值为 \(v\) 的数,那么有三种可能,要么所有的 \(v\) 都在 AND 一边中,要么所有 \(v\) 都在 OR 一边中,要么一部分在 AND 中一部分在 OR 中,还是讨论一下就行了。时间复杂度 \(nQv\),无法通过。
考虑优化,注意到我们枚举时其实并不用精确到 \(v\) 具体的值。我们如果固定了 \(v\) 的 popcount,不妨称之为 \(p\),那么所有 popcount \(<p\) 的值都要放到 OR 集合中,所有 popcount \(>p\) 的值都要放到 AND 集合中,而对于 popcount \(=p\) 的值,它必须为同一个数,否则它们 AND/OR 起来要么 popcount 不为 \(p\),要么不相同。因此我们考虑对于每组区间维护以下三个数组:
- \(\text{AND}_i\):所有 popcount \(=i\) 的数的 bitwise and。
- \(\text{OR}_i\):所有 popcount \(=i\) 的数的 bitwise or。
- \(\text{cnt}_i\):有多少个 popcount \(=i\) 的数。
那么我们求出这三个数组以后,再扫一遍求出 AND 的后缀 and 和 OR 的前缀 or,那么根据上面的过程,我们在 \(\Theta(1)\) 的时间内对一个 popcount \(p\) 计算出它是否可能成为答案。
于是问题就转化为如何高效维护上述三个数组。一个非常直观的想法是线段树,即,对于每种 popcount,建一棵线段树维护区间 and 和区间 or,但由于常数较大,可能无法通过此题(或许卡卡常应该能过?)。注意到这题不带修并且不强制在线,因此考虑离线猫树分治,具体来说我们考虑分治,每次分治一个区间 \([l,r]\) 时我们设 \(mid=\lfloor\dfrac{l+r}{2}\rfloor\),然后我们对每个 \(i\in[l,mid]\) 预处理出 \([i,mid]\) 的 bitwise and 和 or,以及对每个 \(i\in[mid+1,r]\) 预处理出 \([mid+1,i]\) 的 bitwise and 和 or,然后处理所有跨过中点的询问,这样可以 \(\Theta(1)\) 求出每组询问的 AND/OR/cnt。总复杂度 \(n\log n\log v\),常数很小,可以通过。
using namespace fastio;
const int MAXN = 1e5;
const int LOG_N = 30;
int n, qu, a[MAXN + 5], res[MAXN + 5];
struct qry {int l, r;} q[MAXN + 5];
int orR[MAXN + 5][LOG_N + 2], andR[MAXN + 5][LOG_N + 2];
int orL[MAXN + 5][LOG_N + 2], andL[MAXN + 5][LOG_N + 2];
int cL[MAXN + 5][LOG_N + 2], cR[MAXN + 5][LOG_N + 2];
void solve(int l, int r, vector<int> qs) {
if (l == r) return; int mid = l + r >> 1;
memset(orR[mid], 0, sizeof(orR[mid]));
memset(orL[mid + 1], 0, sizeof(orL[mid + 1]));
memset(cR[mid], 0, sizeof(cR[mid]));
memset(cL[mid + 1], 0, sizeof(cL[mid + 1]));
memset(andR[mid], 0xff, sizeof(andR[mid]));
memset(andL[mid + 1], 0xff, sizeof(andL[mid + 1]));
for (int i = mid + 1; i <= r; i++) {
memcpy(orR[i], orR[i - 1], sizeof(orR[i]));
memcpy(andR[i], andR[i - 1], sizeof(andR[i]));
memcpy(cR[i], cR[i - 1], sizeof(cR[i]));
orR[i][__builtin_popcount(a[i])] |= a[i];
andR[i][__builtin_popcount(a[i])] &= a[i];
cR[i][__builtin_popcount(a[i])]++;
}
for (int i = mid; i >= l; i--) {
memcpy(orL[i], orL[i + 1], sizeof(orL[i]));
memcpy(andL[i], andL[i + 1], sizeof(andL[i]));
memcpy(cL[i], cL[i + 1], sizeof(cL[i]));
orL[i][__builtin_popcount(a[i])] |= a[i];
andL[i][__builtin_popcount(a[i])] &= a[i];
cL[i][__builtin_popcount(a[i])]++;
}
vector<int> L, R, A;
for (int x : qs) {
if (q[x].r <= mid) L.pb(x);
else if(q[x].l > mid) R.pb(x);
else A.pb(x);
}
for (int id : A) {
static int AND[LOG_N + 2], OR[LOG_N + 2], cnt[LOG_N + 2];
int ql = q[id].l, qr = q[id].r;
for (int i = 0; i <= LOG_N; i++) {
AND[i] = andL[ql][i] & andR[qr][i];
OR[i] = orL[ql][i] | orR[qr][i];
cnt[i] = cL[ql][i] + cR[qr][i];
}
static int sOR[LOG_N + 2], sAND[LOG_N + 2], prec[LOG_N + 2], sufc[LOG_N + 2];
sOR[0] = OR[0]; prec[0] = cnt[0];
for (int i = 1; i <= LOG_N; i++) {
sOR[i] = sOR[i - 1] | OR[i];
prec[i] = prec[i - 1] + cnt[i];
}
sAND[LOG_N + 1] = 0xffffffff; sufc[LOG_N + 1] = 0;
for (int i = LOG_N; ~i; i--) {
sAND[i] = sAND[i + 1] & AND[i];
sufc[i] = sufc[i + 1] + cnt[i];
}
// printf("%d:\n", id);
// for (int i = 0; i <= LOG_N; i++) printf("%d%c", AND[i], " \n"[i == LOG_N]);
// for (int i = 0; i <= LOG_N; i++) printf("%d%c", OR[i], " \n"[i == LOG_N]);
// for (int i = 0; i <= LOG_N; i++) printf("%d%c", cnt[i], " \n"[i == LOG_N]);
for (int i = 1; i <= LOG_N; i++) {
if (OR[i] == 0) {
res[id] |= (sAND[i + 1] == sOR[i - 1] && prec[i - 1] && sufc[i + 1]);
} else if (__builtin_popcount(OR[i]) == i) {
if (cnt[i] >= 2) {
res[id] |= ((sAND[i + 1] & OR[i]) == (sOR[i - 1] | OR[i]));
}
res[id] |= ((sAND[i + 1] & OR[i]) == sOR[i - 1] && prec[i - 1]);
res[id] |= ((sOR[i - 1] | OR[i]) == sAND[i + 1] && sufc[i + 1]);
}
}
if (cnt[0] >= 2) res[id] = 1;
if (cnt[0] == 1 && sAND[1] == 0) res[id] = 1;
}
solve(l, mid, L); solve(mid + 1, r, R);
}
int main() {
freopen("seq.in", "r", stdin);
freopen("seq.out", "w", stdout);
read(n); read(qu);
for (int i = 1; i <= n; i++) read(a[i]);
for (int i = 1; i <= qu; i++) scanf("%d%d", &q[i].l, &q[i].r);
vector<int> all; for (int i = 1; i <= qu; i++) all.pb(i);
solve(1, n, all);
for (int i = 1; i <= qu; i++) printf("%s\n", (res[i]) ? "YES" : "NO");
return 0;
}
/*
5 15
0 1 1 3 2
1 1
1 2
1 3
1 4
1 5
2 2
2 3
2 4
2 5
3 3
3 4
3 5
4 4
4 5
5 5
75 1
29 48 55 14 33 4 49 3 36 8 49 9 26 42 4 54 10 10 13 20 15 61 1 35 26 11 41 22 45 49 36 18 33 32 34 50 33 60 42 5 45 29 21 14 24 17 16 3 11 24 30 15 0 39 20 17 7 38 28 27 4 50 50 51 47 28 42 21 18 39 40 47 15 14 38
1 75
*/