1. 程式人生 > 實用技巧 >【題解】「CF241B」Friends

【題解】「CF241B」Friends

「CF241B」Friends

trie上亂玩.

傳送門

異或粽子加強版.

正篇

我們可以先求出第$k$大的異或值$K$(在那之前k*=2,方便處理兩個順序相反的數對產生的貢獻),再計算所有比$K$大的異或值的和。

對於求$K$,我們可以建一棵$01trie$,然後在從高到低掃的同時記錄每個數當前到達的樹點(初始為根),每次用它們計算當前位為$1$的$cnt$,判斷$cnt$與$k$的大小關係,並根據結果計算$K$的這一位和更新樹點們(?)。

然後我們可以發現,對於一個數$x>K$,一定在二進位制下有第$i$位$x$為$1$,$K$為$0$,而第大於$i$位有$x$取值$=$$K$取值。

於是問題變得簡單了:同樣的方法記錄樹點,若當前位$K$為$0$,則我們計算當前異或值為$1$對答案的貢獻。這個東西是在$trie$上子樹進行對答案的計算的,所以可以記錄子樹裡每一位不同取值的個數,但是不太好算QAQ。我們發現可以對原陣列排序並按該順序進行插入,於是子樹對應的區間就是連續的,可以在原陣列上字首和簡化演算法了!於是這道題就過了(。

程式碼

大概就和題解裡講的差不多,但細節蠻多的。(借鑑了不少別人部落格QAQ

#include <bits/stdc++.h>
#define Ri register int
#define FOR(i, j, k) for(Ri i = (j); i <= (k); i++)
#define DEC(i, j, k) for(Ri i = (j); i >= (k); i--)
#define ll long long int
using namespace std;

const int maxn = 5e4 + 10, maxk = 32, M = 1e9 + 7
, iv2 = 5e8 + 4; int n, a[maxn]; int ch[maxn * maxk][2], lb[maxn * maxk], rb[maxn * maxk], rt, nc; int sum[maxn][maxk][2], siz[maxn * maxk]; ll m; inline void insert(int x) { if(!rt) rt = ++nc, lb[rt] = 1, rb[rt] = n; int k = rt; siz[rt]++; DEC(d, 29, 0) { if(!ch[k][(a[x] >> d) & 1
]) ch[k][(a[x] >> d) & 1] = ++nc, lb[ch[k][(a[x] >> d) & 1]] = x; k = ch[k][(a[x] >> d) & 1]; rb[k] = x; siz[k]++; } } inline ll calc(int k, int v) { if(!k) return 0; ll res = 0; DEC(d, 29, 0) { ll t = sum[rb[k]][d][((v >> d) & 1) ^ 1] - sum[lb[k] - 1][d][((v >> d) & 1) ^ 1]; res = (res + t * (1 << d) % M) % M; } return res; } int to[maxn * maxk]; int main() { scanf("%d%lld", &n, &m); m <<= 1LL; FOR(i, 1, n) scanf("%d", &a[i]); sort(a + 1, a + n + 1); lb[0] = 1, rb[0] = 0; FOR(i, 1, n) insert(i); //get sum[] FOR(i, 1, n) { FOR(d, 0, 29) { sum[i][d][0] = sum[i - 1][d][0] + (((a[i] >> d) & 1) == 0); sum[i][d][1] = sum[i - 1][d][1] + (((a[i] >> d) & 1) == 1); } } //get k-th xor int low = 0; FOR(i, 1, n) to[i] = rt; DEC(d, 29, 0) { ll cnt = 0; FOR(i, 1, n) cnt += siz[ch[to[i]][((a[i] >> d) & 1) ^ 1]]; if(cnt >= m) { low |= (1 << d); FOR(i, 1, n) { to[i] = ch[to[i]][((a[i] >> d) & 1) ^ 1]; } }else { m -= cnt; FOR(i, 1, n) { to[i] = ch[to[i]][(a[i] >> d) & 1]; } } } //Answer! ll ans = m % M * low % M; FOR(i, 1, n) to[i] = rt; DEC(d, 29, 0) { int x = (low >> d) & 1; if(!x) { FOR(i, 1, n) { ans = (ans + calc(ch[to[i]][((a[i] >> d) & 1) ^ 1], a[i])) % M; } } FOR(i, 1, n) { to[i] = ch[to[i]][((a[i] >> d) & 1) ^ x]; } } printf("%lld", ans * iv2 % M); return 0; }