BZOJ 3689: 异或之
字典树可以$o(logn)查找第k大$
使用$可持久化Trie 区间查找第k大,然后首先把每个数异或之后的最小丢进小根堆中,然后一个一个取出,取出后就再丢次小,一共取k次$
总的时间复杂度为$O(klogn)$
本来的考虑是 先找出第k大,然后在$Trie上DFS把小于这个数的全丢进vector 然后发现会有很多无用状态会搜索到,T掉$
1 #include <bits/stdc++.h> 2 using namespace std; 3 4 #define N 100010 5 int n, k, arr[N], kth; 6 struct node 7 { 8 int base, num, pos, l, r; 9 node () {} 10 node (int base, int num, int pos, int l, int r) : base(base), num(num), pos(pos), l(l), r(r) {} 11 bool operator < (const node &r) const { return num > r.num; } 12 }; 13 priority_queue <node> q; 14 15 namespace TRIE 16 { 17 int rt[N], cnt; 18 struct node 19 { 20 int son[2], cnt; 21 }a[N * 50]; 22 void init() 23 { 24 a[1].son[0] = a[1].son[1] = a[1].cnt = 0; 25 cnt = 0; 26 } 27 void insert(int &now, int pre, int x) 28 { 29 now = ++cnt; 30 a[now] = a[pre]; 31 int root = now; ++a[root].cnt; 32 for (int i = 30; i >= 0; --i) 33 { 34 int id = (x >> i) & 1; 35 a[++cnt] = a[a[root].son[id]]; 36 ++a[cnt].cnt; 37 a[root].son[id] = cnt; 38 root = cnt; 39 } 40 } 41 int find_kth(int x, int k, int l, int r) 42 { 43 int res = 0; 44 int tl = rt[l], tr = rt[r]; 45 for (int s = 30; s >= 0; --s) 46 { 47 int id = (x >> s) & 1; 48 int sum = a[a[tr].son[id]].cnt - a[a[tl].son[id]].cnt; 49 if (sum < k) 50 { 51 k -= sum; 52 res |= 1 << s; 53 tr = a[tr].son[id ^ 1]; 54 tl = a[tl].son[id ^ 1]; 55 } 56 else 57 { 58 tr = a[tr].son[id]; 59 tl = a[tl].son[id]; 60 } 61 } 62 return res; 63 } 64 } 65 66 int main() 67 { 68 while (scanf("%d%d", &n, &k) != EOF) 69 { 70 for (int i = 1; i <= n; ++i) scanf("%d", arr + i); 71 TRIE::init(); 72 for (int i = 1; i <= n; ++i) TRIE::insert(TRIE::rt[i], TRIE::rt[i - 1], arr[i]); 73 for (int i = 1; i < n; ++i) q.push(node(arr[i], TRIE::find_kth(arr[i], 1, i, n), 2, i, n)); 74 for (int i = 1; i <= k; ++i) 75 { 76 node top = q.top(); q.pop(); 77 printf("%d%c", top.num, " \n"[i == k]); 78 q.push(node(top.base, TRIE::find_kth(top.base, top.pos, top.l, top.r), top.pos + 1, top.l, top.r)); 79 } 80 } 81 return 0; 82 }