gym102331B Bitwise Xor
和我找到的题解都不同的做法。感觉简单一些。
首先将 \(a\) 排序,从高位往低位考虑,假设考虑第 \(p\) 位,不难发现这时序列按照第 \(p\) 位取值被划分为两部分,我们注意到如果 \(x\) 的这一位是 \(0\) 那么这两部分各取两个数异或起来一定满足限制,故两部分互互不影响,可以分别递归下去把方案数乘起来就是总方案数。而如果 \(x\) 的这一位是 \(1\) 那么我们根据鸽巢原理至多只能取两个数,这时候就相当于取两个数异或大于等于 \(x\),可以直接用一个 Trie 做。然后就做完了。注意复杂度不是 \(O(n \log a)\) 而是 \(O(n \log (na))\)。
const int N = 3e5 + 10;
int n;
LL x, a[N];
struct Trie {
int ch[N * 60][2], tr[N * 60];
int tot = 1;
int newNode() {
++tot;
tr[tot] = 0;
ch[tot][0] = ch[tot][1] = 0;
return tot;
}
void clear() {
tot = 0; newNode();
}
void insert(LL x) {
int u = 1;
for(int i = 59; ~i; --i) {
++tr[u];
int t = x >> i & 1;
if(!ch[u][t]) ch[u][t] = newNode();
u = ch[u][t];
}
++tr[u];
}
int query(LL x, LL y) {
int u = 1, res = 0;
for(int i = 59; ~i && u; --i) {
int t = y >> i & 1, p = x >> i & 1;
if(t == 0) {
res += tr[ch[u][p ^ 1]];
u = ch[u][p];
} else u = ch[u][p ^ 1];
}
return res + tr[u];
}
}trie;
const int P = 998244353;
void add(int &x, int y) {
(x += y) >= P ? x -= P : 0;
}
void sub(int &x, int y) {
(x -= y) < 0 ? x += P : 0;
}
int solve(int l, int r, int p) {
if(l > r) return 0;
if(x >> p & 1) {
int ans = r - l + 1;
trie.clear();
for(int i = l; i <= r; ++i) {
a[i] &= (1ll << (p + 1)) - 1;
add(ans, trie.query(a[i], x)), trie.insert(a[i]);
}
return ans;
}
int mid;
for(mid = l; mid <= r; ++mid)
if(a[mid] >> p & 1) break;
return (1ll * (solve(l, mid - 1, p - 1) + 1) * (solve(mid, r, p - 1) + 1) % P - 1 + P) % P;
}
int main() {
read(n, x);
if(x == 0) {
int ans = 1;
for(int i = 1; i <= n; ++i)
add(ans, ans);
printf("%d\n",ans - 1);
return 0;
}
for(int i = 1; i <= n; ++i)
read(a[i]);
sort(a + 1, a + n + 1);
printf("%d\n",solve(1, n, 59));
}
标签:ch,Xor,int,void,tr,gym102331B,Bitwise,tot,return
From: https://www.cnblogs.com/DCH233/p/17742871.html