1 条题解
-
0
题意简述
给定 个互不相同的整数 ,求所有无序对 的 的十进制位数之和。
关键点
十进制位数 (对 ),如果 则位数为 1,但这里 互不相同,所以 可能为 0 吗? 只有当 时异或为 0,但 互不相同,所以异或结果不会为 0,因此位数至少为 1。
我们要求:
ans
∑ i < j ( ⌊ log 10 ( a i ⊕ a j ) ⌋ + 1 ) ans= i<j ∑ (⌊log 10 (a i ⊕a j )⌋+1)
直接计算不可行
, 会超时。
思路转换
十进制位数 满足:
10 k − 1 ≤ a i ⊕ a j < 10 k 10 k−1 ≤a i ⊕a j <10 k
即:
k
⌊ log 10 ( a i ⊕ a j ) ⌋ + 1 k=⌊log 10 (a i ⊕a j )⌋+1 所以 只与异或值的大小范围有关。
我们可以按异或值的二进制高位来分组,但这里要求十进制位数,所以最好直接按十进制范围分组。
分组统计
设 ,那么异或值在 内的对,其十进制位数就是 。
因此:
ans
∑ k ≥ 1 k ⋅ ( 异或值在 [ 10 k − 1 , 10 k − 1 ] 内的对数 ) ans= k≥1 ∑ k⋅(异或值在 [10 k−1 ,10 k −1] 内的对数)
问题转化为
对每个 ,计算有多少对 满足:
10 k − 1 ≤ a i ⊕ a j < 10 k 10 k−1 ≤a i ⊕a j <10 k
利用 Trie 树按位统计
我们可以用 二进制 Trie(01-Trie)来统计异或值在某个区间内的对数。
对于区间 ,我们可以在 Trie 上遍历,统计异或值 的数目。
但这里 , 在二进制下不是整齐的 2 的幂,所以直接统计比较麻烦。
更简单的方法:按最高位位置
注意 ,所以十进制位数 对应二进制位数大约在 $[ \lceil (k-1) \log_2 10 \rceil, \lceil k \log_2 10 \rceil )$ 之间。
更精确地, 的二进制位数是 , 的二进制位数是 。
我们可以按二进制最高位来分组,然后映射到十进制位数。
算法步骤
对每个 ,计算它的二进制最高位位置 (从 0 开始计数,即 )。
异或结果的最高位位置 等于 或更高,具体取决于它们前缀的相等情况。
更直接的方法: 在 Trie 中插入所有数,然后对每个 ,在 Trie 中查询与 异或后结果的最高位位置分布。
高效做法
在 Trie 上遍历时,我们可以记录当前节点下有多少数,并且知道当前异或值的前缀。 当我们知道异或值的二进制长度 时,可以推断它的十进制位数 :
二进制长度 对应的十进制位数 满足:
⌈ l e n ⋅ log 10 2 ⌉ ≤ k ≤ ⌊ l e n ⋅ log 10 2 ⌋ + 1 ⌈len⋅log 10 2⌉≤k≤⌊len⋅log 10 2⌋+1 实际上更精确: 二进制长度 → 十进制位数 。
最终方案
将 按二进制插入 Trie。
对每个 ,在 Trie 中遍历,当走到深度 (从高位到低位)时,如果当前异或值的这一位为 1,说明异或值的最高位就是 (从 0 开始),那么它的二进制长度 = 。
根据二进制长度 计算十进制位数 :
k
⌊ ( l e n − 1 ) ⋅ log 10 2 ⌋ + 1 k=⌊(len−1)⋅log 10 2⌋+1 累加 到答案。
时间复杂度
,因为 。
示例代码(框架)
cpp #include <bits/stdc++.h> using namespace std; using ll = long long;
const int MAXB = 60; // 2^60 > 1e18 const double log10_2 = log10(2);
struct TrieNode { int cnt; TrieNode* ch[2]; TrieNode() : cnt(0) { ch[0] = ch[1] = nullptr; } };
void insert(TrieNode* root, ll x) { TrieNode* cur = root; for (int b = MAXB-1; b >= 0; b--) { int bit = (x >> b) & 1; if (!cur->ch[bit]) cur->ch[bit] = new TrieNode(); cur = cur->ch[bit]; cur->cnt++; } }
ll query(TrieNode* root, ll x) { TrieNode* cur = root; ll ans = 0; for (int b = MAXB-1; b >= 0; b--) { int bit = (x >> b) & 1; // 如果走相反分支,这一位异或结果为1,那么最高位就是b if (cur->ch[bit^1]) { int len = b+1; // 二进制长度 int dec_len = floor((len-1) * log10_2) + 1; ans += dec_len * cur->ch[bit^1]->cnt; } if (!cur->ch[bit]) break; cur = cur->ch[bit]; } return ans; }
int main() { int n; cin >> n; vector a(n); for (int i = 0; i < n; i++) cin >> a[i];
TrieNode* root = new TrieNode(); ll ans = 0; for (int i = 0; i < n; i++) { ans += query(root, a[i]); insert(root, a[i]); } cout << ans << endl; return 0;}
总结
核心是将十进制位数转化为二进制长度,再用 Trie 统计最高位位置。
利用 进行二进制与十进制位数转换。
时间复杂度 ,可过 。
- 1
信息
- ID
- 5368
- 时间
- 1000ms
- 内存
- 256MiB
- 难度
- 10
- 标签
- 递交数
- 1
- 已通过
- 1
- 上传者