Problem
acm.hdu.edu.cn/showproblem.php?pid=6059
Reference
HDU 6059 Kanade’s trio (字典树, 2017 Multi-Univ Training Contest 3)
2017 Multi-University Training Contest 3 solutions BY 洪华敦(1004题)
Meaning
给一个序列 {An} ,问有多少个有序数对(i, j, k),满足:
- i < j < k
- Ai⨁Aj<Aj⨁Ak
Analysis
从第 2 个条件看到,Aj 在不等式两端都出现,应该是作为一个调节因子的存在,于是先找 Ai 和 Ak,通过它们的关系来“夹”出符合的 Aj。
看 Ai 和 Ak 的关系,考虑它们最高的不同的位(因为低位影响不及高位大,而相同位的用 Aj 调不出差异),假设是第 p 位,有两种情况:
- Ai[p]=1,Ak[p]=0 。要满足第 2 条不等式,就要让 Aj[p]=1 ,根据异或性质,这样可以消掉 Ai 在 p 位这个 1,并且让 Aj 的 p 位变 1,于是满足不等式;
- Ai[p]=0,Ak[p]=1 。这样 Aj[p] 就要为 1,分析同上;
于是看出 Ai 和 Aj 的一个关系:
Ai[p]=Aj[p]
(p 还是表示 Ai 和 Ak 最高的不同位)。
于是从前往后地把数插入到字典树中,把当前正在插入的数当做 Ak 来算边插入边算贡献。
上面说要考虑 Ai 和 Ak 最高的不同的位 p,那就把当前正在插入的位当做第 p 位,那么对于这个 Ak 来说,可选的 Ai 就是树上同一条路径下来的、在当前这一位才分岔出去的另一棵子树里记录的那些数。
然后找 Aj,由于 Ai 和 Aj 在第 p 位相同,所以上述 Ai 那棵子树里的数同时也能作为 Aj(这样的 Aj 高位与 Ak 相同);
Aj 还可以从别的子树里找,也就是高位与 Ak 不同的、但第 p 位还是与 Ai 相同的数。可以开个数组记录:num[i][j] = 第 i 位值为 j(0/1)的数的个数
。但这些数未必符合 i < j 的条件。字典树结点里多记一个值,即参考博客里的ext
,说的是如果把当前的节点当做 Ai(而不是 Ak)的话,那在它之前就已经插入的 p 位也为这个值的数,虽然 p 位值相同,但违反 i < j 的规定,把这些数的个数记下,在算贡献时减去。
Code
#include <cstdio>
#include <cstring>
using namespace std;
const int N = 500000, BIT = 30;
struct node
{
int cnt;
int sj; // smaller j,p 位相同但下标比较小的数的个数
int nxt[2]; // child node
} trie[N*BIT+1];
int sz;
int dig[BIT]; // 拆下来的二进制位
int num[BIT][2]; // num[i][j]:已插入的第 i 位值为 j 的数的个数
long long ans;
void update(int p, int pre)
{
// Ai 子树中任选两个分别当 Ai 和 Aj
ans += (long long)trie[p].cnt * (trie[p].cnt - 1) >> 1;
// Ai 子树上的只做 Ai,而在其它子树找 Aj,但要去除 i > j 的
ans += (long long)trie[p].cnt * (pre - trie[p].cnt) - trie[p].sj;
}
void insert()
{
for(int b = BIT - 1, p = 0, d; ~b; --b)
{
d = dig[b];
if(!trie[p].nxt[d])
trie[p].nxt[d] = sz++;
if(trie[p].nxt[1-d])
update(trie[p].nxt[1 - d], num[b][1 - d]);
p = trie[p].nxt[d];
++trie[p].cnt;
trie[p].sj += num[b][d] - trie[p].cnt;
}
}
int main()
{
int T;
scanf("%d", &T);
while(T--)
{
int n;
scanf("%d", &n);
memset(num, 0, sizeof num);
memset(trie, 0, sizeof trie);
sz = 1;
ans = 0;
for(int a; n--; )
{
scanf("%d", &a);
for(int b = 0; b < BIT; ++b, a >>= 1)
{
++num[b][a & 1];
dig[b] = a & 1;
}
insert();
}
printf("%I64d\n", ans);
}
return 0;
}
题目的trio
莫非就是在暗示trie
?