题解:P10743 [SEERC2020] AND = OR

· · 题解

给出一个单 \log 的离线做法。

考虑枚举 OR 和 AND 相等的那个值是什么。

直接枚举值复杂度不可承受,寻找一个和原值具有相似性质且小值域的东西。

枚举 \operatorname{popcount}。不断加入新元素的过程中,序列 OR \operatorname{popc} 单调不降,序列 AND \operatorname{popc} 单调不增。(\operatorname{popc} 简写)

设 OR 和 AND 的 \operatorname{popcount} 都等于 p,那么所有 \operatorname{popc}(a_i)<p 的都应该放进 OR,\operatorname{popc}(a_i)>p 的都应该被放进 AND。

对每一个二进制位建一个线段树,处理出区间 $[l,r]$ 在 $p\in[0,30]$ 上的前缀 OR。判这个前缀 OR 是否是空集可以线段树顺便维护一个区间个数和,然后算个数和的前缀和。$\operatorname{popc}(a_i)>p$ 的后缀 AND 同理。 考虑 $\operatorname{popc}(a_i)=p$ 的情况。 显然多次 OR 或 AND 同一个数不会再次改变值,先关注数的种类。 若 $\operatorname{popc}(a_i)=p$ 的 $a_i$ 只有一种数,全给 OR、全给 AND、若个数 $\ge 2$ 可以两个都给,枚举三种情况。 + 这里只需要查 $\ge 2$,可以预处理每个数向后第一个相等位置,判断位置 $\le r$。 若有两种数,对于 $a_i\ne a_j,\,\operatorname{popc}(a_i)=\operatorname{popc}(a_j)$,有 $\operatorname{popc}(a_i\,|\,a_j)>\operatorname{popc}(a_i)=p,\,\operatorname{popc}(a_i\,\&\,a_j)<\operatorname{popc}(a_i)=p$,根据前面 OR 只能放 $\operatorname{popc}<p$ 的讨论,AND 对称同理,所以两种数一定只能各自给其中一边,设两种数是 $a_i,\,a_j$,OR 和 AND 的 $\operatorname{popc}$ 一定分别是 $\ge p$ 和 $\le p$ 的,$a_i=a_j$ 时两个 $\operatorname{popc}$ 才有可能取等,这与 $a_i\ne a_j$ 矛盾。所以两种数一定不合法。 对于三种及以上,同样不会合法,所以种类要 $\le 1$。 预处理枚举到每个 $\operatorname{popcount}=p$ 时每个位置向后两个互不相同且 $\operatorname{popc}=p$ 的数,就可以判上述情况了。 复杂度 $O(n\log V+q\log V\log n)$。 考虑优化,瓶颈在于 $\log V$ 个线段树每次询问时的查询。 考虑分治,将询问离线挂在分治树上,每次处理跨过当前分治区间 $mid$ 的询问,猫树的思想,将询问 $[ql,qr]$ 拆为 $[l,mid]$ 上的后缀 $[ql,mid]$,和 $[mid+1,r]$ 上的前缀 $[mid+1,qr]$,直接扫过 $[l,mid$] 得到每个后缀的答案,贡献到询问上,$[mid+1,r]$ 同理。 时间复杂度 $O(n\log n+ q\log V)$。 ``` cpp #include <bits/stdc++.h> using namespace std; #define rep(i, f, t) for (int i(f); i <= t; ++i) #define re(i, t) for (int i(1); i <= t; ++i) #define per(i, t, f) for (int i(t); i >= f; --i) #define pe(i, t) for (int i(t); i >= 1; --i) typedef pair<int, int> pii; #define pb push_back #define mkp make_pair #define ls(x) (x << 1) #define rs(x) (x << 1 | 1) #define fi first #define se second const int N = 1e5 + 5; int n, Q, a[N], pc[N]; int ini[N]; int A[31], B[31], Ac[31], Bc[31], C[31]; using Pos = tuple<int, int>; Pos P[31][N]; int nxt[N]; map<int, int> buc; pii qry[N]; vector<int> t[N * 4]; void putqry(int rt, int l, int r, int ql, int qr, int id) { if (l == r) { t[rt].pb(id); return; } int mid = (l + r) / 2; if (qr <= mid) { putqry(ls(rt), l, mid, ql, qr, id); return; } if (ql > mid) { putqry(rs(rt), mid + 1, r, ql, qr, id); return; } t[rt].pb(id); } struct Dat { int Or, And, cnt; } Res[31][N]; int tmp[31]; vector<int> vec[N]; void work(int rt, int l, int r) { if (l == r) { for (int id : t[rt]) { Res[pc[l]][id].Or |= a[l]; Res[pc[l]][id].And &= a[l]; Res[pc[l]][id].cnt++; } return; } int mid = (l + r) / 2; for (int id : t[rt]) vec[qry[id].fi].pb(id), vec[qry[id].se].pb(id); rep(i, 0, 30) tmp[i] = 0; rep(i, mid + 1, r) { tmp[pc[i]] |= a[i]; for (int id : vec[i]) rep(p, 0, 30) Res[p][id].Or |= tmp[p]; } rep(i, 0, 30) tmp[i] = 0; per(i, mid, l) { tmp[pc[i]] |= a[i]; for (int id : vec[i]) rep(p, 0, 30) Res[p][id].Or |= tmp[p]; } rep(i, 0, 30) tmp[i] = (1 << 30) - 1; rep(i, mid + 1, r) { tmp[pc[i]] &= a[i]; for (int id : vec[i]) rep(p, 0, 30) Res[p][id].And &= tmp[p]; } rep(i, 0, 30) tmp[i] = (1 << 30) - 1; per(i, mid, l) { tmp[pc[i]] &= a[i]; for (int id : vec[i]) rep(p, 0, 30) Res[p][id].And &= tmp[p]; } rep(i, 0, 30) tmp[i] = 0; rep(i, mid + 1, r) { tmp[pc[i]]++; for (int id : vec[i]) rep(p, 0, 30) Res[p][id].cnt += tmp[p]; } rep(i, 0, 30) tmp[i] = 0; per(i, mid, l) { tmp[pc[i]]++; for (int id : vec[i]) rep(p, 0, 30) Res[p][id].cnt += tmp[p]; } for (int id : t[rt]) vec[qry[id].fi].clear(), vec[qry[id].se].clear(); work(ls(rt), l, mid); work(rs(rt), mid + 1, r); } int main() { ios::sync_with_stdio(false); cin >> n >> Q; re(i, n) cin >> a[i]; pe(i, n) { nxt[i] = buc[a[i]]; if (nxt[i] == 0) nxt[i] = n + 1; buc[a[i]] = i; } re(i, n) pc[i] = __builtin_popcount(a[i]); rep(p, 0, 30) { re(i, n) { if (pc[i] == p) ini[i] = a[i]; else ini[i] = -1; } Pos now = make_tuple(n + 1, n + 1); pe(i, n) { if (ini[i] != -1) { int u = 0, v = 0; tie(u, v) = now; if (u == n + 1) u = i; else if (v == n + 1) { if (a[u] == a[i]) u = i; else v = u, u = i; } else { if (a[u] == a[i]) u = i; else if (a[v] == a[i]) v = u, u = i; else v = u, u = i; } now = tie(u, v); } P[p][i] = now; } } re(i, Q) { int l = 0, r = 0; cin >> l >> r; qry[i] = mkp(l, r); putqry(1, 1, n, l, r, i); } rep(p, 0, 30) re(i, Q) { Res[p][i].Or = 0; Res[p][i].And = (1 << 30) - 1; Res[p][i].cnt = 0; } work(1, 1, n); re(qid, Q) { int l = 0, r = 0; tie(l, r) = qry[qid]; if (l == r) { cout << "NO\n"; continue; } rep(p, 0, 30) { Dat tt = Res[p][qid]; A[p] = tt.Or, B[p] = tt.And; Ac[p] = Bc[p] = C[p] = tt.cnt; } rep(p, 1, 30) A[p] |= A[p - 1], Ac[p] += Ac[p - 1]; per(p, 29, 0) B[p] &= B[p + 1], Bc[p] += Bc[p + 1]; int Ans = 0; rep(p, 0, 30) { int As = 0, Bs = (1 << 30) - 1, tA = 0, tB = 0; if (p > 0) As = A[p - 1], tA = (Ac[p - 1] > 0); if (p < 30) Bs = B[p + 1], tB = (Bc[p + 1] > 0); int Cnt = 0; int u = 0, v = 0; tie(u, v) = P[p][l]; Cnt = (u <= r) + (v <= r); u = min(u, r); v = min(v, r); if (Cnt == 0) { if (As == Bs && tA && tB) { Ans = 1; break; } } if (Cnt == 1) { int ok = (((As | a[u]) == Bs && tB) | (As == (Bs & a[u]) && tA)); if (nxt[u] <= r) ok |= ((As | a[u]) == (Bs & a[u])); if (ok) { Ans = 1; break; } } } if (Ans) cout << "YES\n"; else cout << "NO\n"; } return 0; } ```