三亩地 三亩地SAN MU DI · CODE DIARY
ARTICLE DETAIL

日记详情

真实记录编程学习的某一天,欢迎挑你感兴趣的翻一翻。

第八届广西大学生程序设计大赛暨2025邀请赛 G题思路分享(trie树)

第八届广西大学生程序设计大赛暨2025邀请赛 G题思路分享(trie树)

https://ac.nowcoder.com/acm/contest/110811/G

题意概述

给定两个长度为 \(n\) 的数组 \(a,b\) 和两个参数 \(k_1,k_2\),求满足:

  • \(i \lt j\)

  • \(k_1 \oplus a_i \oplus a_j \lt k_2 \oplus b_i \oplus b_j\)

的点对数量。

思路

\(k_1 \oplus a_i \oplus a_j = X\)\(k_2 \oplus b_i \oplus b_j = Y\)。原不等式相当于在 \(X \oplus Y\) 的最高有效位 \(d\) 上,\(X\) 在该位上为 \(0\)\(Y\) 在该位上为 \(1\)

\(X \oplus Y\)\(d\) 之前的位上为 \(0\)\(X\) 的第 \(d\) 位为 \(0\),即 \(k_1 \oplus a_i \oplus a_j\)\(d\) 位为 \(0\)

\(X \oplus Y\) 的表达中可以将 \(i,j\) 分离,考虑 \(trie\) 树,插入 \(a_i \oplus b_i\)。需要知道 \(X\) 是否为 \(0\),统计 \(a_i\) 在该位为 \(0\) 和为 \(1\) 的数量。

\(trie\) 树上累加贡献即可,跳转时走让 \(X \oplus Y\) 在该位为 \(0\) 的边,如果存在让 \(X \oplus Y\) 在该位为 \(1\) 的边,就以该位为最高位计算一次贡献。

时间复杂度 \(\mathcal{O}(32n)\)

代码

//author:kzssCCC#include <bits/stdc++.h>
using namespace std;
using ll = long long;void solve(){int n,k1,k2;cin >> n >> k1 >> k2;vector<int> a(n+1);for (int i=1;i<=n;i++){cin >> a[i];}	vector<int> b(n+1);for (int i=1;i<=n;i++){cin >> b[i];}vector<array<int,2>> next{{-1,-1}};vector<array<int,2>> cnt{{0,0}};ll res = 0;for (int i=1;i<=n;i++){{int u = 0;for (int j=31;j>=0;j--){if (next[u][((a[i]^b[i]^k1^k2)>>j&1)^1]!=-1){res += cnt[next[u][((a[i]^b[i]^k1^k2)>>j&1)^1]][(a[i]^k1)>>j&1];}if (next[u][(a[i]^b[i]^k1^k2)>>j&1]!=-1){u = next[u][(a[i]^b[i]^k1^k2)>>j&1];}else break;}}{int u = 0;for (int j=31;j>=0;j--){int cj = (a[i]^b[i])>>j&1;if (next[u][cj]==-1){next.push_back({-1,-1});next[u][cj] = next.size()-1;cnt.push_back({0,0});}cnt[next[u][cj]][a[i]>>j&1]++;u = next[u][cj];}}}cout << res << '\n';
}int main(){ios::sync_with_stdio(false);cin.tie(0);int t = 1;// cin >> t;while (t--) solve();return 0;
}
← 返回列表