鏈接
http://acm.hdu.edu.cn/showproblem.php?pid=6059
題意
給出
思路
事先把所有數字插入字典樹中,用字典樹維護
考慮當前位置
對於cnt[i][j]
數組記錄下第i位爲j的數之前出現了幾次,那麼在插入時,對於這一位置cnt[i][nxt ^ 1]
就是此時符合條件的
當我們把一個數從字典樹中去掉時,也要考慮去掉這個數留下來的統計值。
這題特殊的地方在於,插入是連續的,之後是連續的刪除,所以在插入完成後可以把cnt[i][j]
數組清空一遍,用來記錄第i位爲j的數被刪除了幾次。
考慮兩個方面:
- 一個是這個數作爲cnt[now][nxt ^ 1]
)就好。(這一步操作在Trie::Insert()
裏面,與插入時的操作類似)
- 還有一個是這個數作爲sum[tmp]
中,這裏面還需去掉被刪去的cnt[i][nxt]
中,現有的val[tmp]
中,這一部分不能被計入答案,相乘,減去。(sum[tmp] - val[tmp] * cnt[i][nxt]
這一步在函數solve()
裏)
希望思路說清楚了,詳見代碼
代碼
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
#define MS(x, y) memset(x, y, sizeof(x))
typedef long long LL;
const int MAXN = 5e5 + 5;
int bits[32];
struct Trie {
int tot, root;
int val[MAXN * 30], ch[MAXN * 30][2];
LL sum[MAXN * 30], cnt[MAXN][2];
int newnode() {
val[tot] = sum[tot] = 0;
ch[tot][0] = ch[tot][1] = -1;
return tot++;
}
void init() {
tot = 0;
root = newnode();
MS(cnt, 0);
}
void Insert(int x, int v) {
int now = root, nxt, tmp;
for (int i = 30; i >= 0; --i) {
nxt = !!(x & bits[i]);
if (ch[now][nxt] == -1) ch[now][nxt] = newnode();
now = ch[now][nxt];
++cnt[i][nxt];
sum[now] += v * cnt[i][nxt ^ 1];
val[now] += v;
}
}
LL solve(int x) {
LL ret = 0;
int now = root, tmp, nxt;
for (int i = 30; i >= 0; --i) {
nxt = !!(x & bits[i]);
tmp = ch[now][nxt ^ 1];
now = ch[now][nxt];
if (tmp != -1) {
ret += sum[tmp] - val[tmp] * cnt[i][nxt];
}
if (now == -1) break;
}
return ret;
}
};
int n;
int a[MAXN];
LL ans;
Trie trie;
int main() {
bits[0] = 1;
for (int i = 1; i < 32; ++i) bits[i] = bits[i - 1] << 1;
int T;
scanf("%d", &T);
while (T--) {
scanf("%d", &n);
for (int i = 1; i <= n; ++i) scanf("%d", a + i);
ans = 0;
trie.init();
for (int i = 1; i <= n; ++i) trie.Insert(a[i], 1);
MS(trie.cnt, 0);
for (int i = 1; i < n; ++i) {
trie.Insert(a[i], -1);
ans += trie.solve(a[i]);
}
printf("%I64d\n", ans);
}
}