题意:
There are T test cases.
1≤T≤20
1≤∑n≤5∗105
0≤A[i]<230
参考:
http://blog.youkuaiyun.com/u013944294/article/details/76577490 ——QQ小炫
看了很多题解...主流的做法是从左向右插入 a[k],对于 a[k] 的每一位,去看与它前面所有位都相同的而当前位不同的 a[i],即与 a[k] 的父亲的另一棵子树。只要 a[j] 的当前位与 a[i] 相同即可满足条件,所以再用一个 cnt[i][j] 记录第 i 位为 j (0/1) 的数字有多少个。另外,还需要处理的一个问题就是 i < j 的要求,这一点我没看太懂...
于是继续找,找到了上面那位博主的文章,觉得这种思路真的是很巧。
主要想法是枚举 a[j], 看有多少个 a[i] 及 a[k] 满足条件。
先将数组中所有数字插入到 trie 树中,此时记录下 sz[rt][1][bit],意为以 rt 为根的子树中下一位为 bit 的有多少个,中间一维的含义之后再解释。
然后再从左向右模拟一遍插入 a[j] 的过程,怎么模拟呢?
这里也引入 cnt 数组,但含义与上面那种不同。这里的 cnt[i][j] 意为更高位的前缀都相同,第 i 位左边为 j ,右边为 j ^ 1 的数对个数。显然,由其含义可知,cnt 累和即为答案。
对于 a[j] 的第 i 位,当我们把 a[j] 向左移(即划入左边那块时),
若其为 0,左 0 右 1 的个数,即 cnt[i][0] 的个数会增加右边第 i 位为 1 的数的个数,即 sz[rt][1][1]
左 1 右 0 的个数,会减去左边左边第 i 位为 1 的个数,即 cnt[i][1] -= sz[rt][0][1]
若其为 1,左 0 右 1 的个数,会减少左边第 i 位为 0 的个数,即 cnt[i][0] -= sz[rt][0][0]
左 1 右 0 的个数,会加上右边第 i 位为 0 的个数,即 cnt[i][1] += sz[rt][1][0]
总结一下 cnt[i][bit] += sz[rt][1][bit ^ 1];
cnt[i][bit ^ 1] -= sz[rt][0][bit ^ 1].
这样,每次进入循环时加上当前的数字作为 a[j] 时的种数,即 cnt[j][bit],然后将 a[j] 移到左边为下一次做准备。
对原Po佩服得五体投地...。
Code:
#include <bits/stdc++.h>
#define maxn 500010
typedef long long LL;
LL cnt[31][2];
int trie[maxn * 31][2], tot, a[500010], sz[maxn * 31][2][2];
void ins(int x) {
int rt = 0;
for (int i = 29; i >= 0; --i) {
int bit = (x >> i) & 1;
if (!trie[rt][bit]) trie[rt][bit] = ++tot;
++sz[rt][1][bit];
rt = trie[rt][bit];
}
}
LL query(int x) {
int rt = 0;
LL ans = 0;
for (int i = 29; i >= 0; --i) {
int bit = (x >> i) & 1;
ans += cnt[i][bit];
cnt[i][bit] += sz[rt][1][bit ^ 1];
cnt[i][bit ^ 1] -= sz[rt][0][bit ^ 1];
// printf("%d : %lld %lld\n", i, cnt[i][0], cnt[i][1]);
--sz[rt][1][bit];
++sz[rt][0][bit];
rt = trie[rt][bit];
}
return ans;
}
void work() {
int n; tot = 0;
memset(cnt, 0, sizeof(cnt));
memset(trie, 0, sizeof(trie));
memset(sz, 0, sizeof(sz));
scanf("%d", &n);
for (int i = 0; i < n; ++i) {
scanf("%d", &a[i]);
ins(a[i]);
}
LL ans = 0;
for (int i = 0; i < n; ++i) ans += query(a[i]);
printf("%lld\n", ans);
}
int main() {
int T;
scanf("%d", &T);
while (T--) work();
return 0;
}