题解 P5283 【[十二省联考2019]异或粽子】

_虹_

2019-04-08 21:14:41

Solution

这里给出一个考场**玄学**做法。 首先我们~~可以看出这题可持久化01trie可做~~。 然后我们可以发现对于答案中的每个区间,~~它必然有一个右端点(废话)~~ 既然每个答案有一个右端点,那么我们只要能对所有可行的右端点,得到可行的最大区间异或和就行了。 把序列弄成前缀异或和,当trie树深度为d时,这可以O(nd)搞出来,O(nlogn)塞进堆里。 当我们弹出一个区间和时,对于这个右端点,它的答案需要更新成次大的区间异或和。 这很明显可以通过trie树节点上记录size,直接求第k大搞定。 ~~但是其实可以不这么做。~~ 当在trie树上找最大异或值时,其实就是贪心在搜索树上找到一条链的过程。 但是这里的贪心和一般的贪心不同,很明显,每层只有两个决策,并且在树深度较深处转向,比更浅处更优。 ~~这不就是dfs的回溯吗~~ 所以我们可以开一个n * d的二维数组作为堆栈。每在堆中取出一个区间异或和,我们就对对应的右端点回溯。 但是看这个数据:3 2 2 ... 它的异或前缀和是:3 1 3 ... 这个3,在异或前缀和里出现了不止一次,但对于trie树,它只出现了一次,如果对于某个右端点,3需要对答案提供不止一次的贡献,喜提Wa。(~~大样例都过不去但是可以80分)~~ ~~好办,维护计数器~~ ### MLE 为啥会mle呢,因为这个做法需要保留对于每个右端点,取答案时trie树上走的路径。算上堆栈,就相当于是开了1颗半可持久化trie。 而且还要记录路径上每个节点走过方向,又是一个2nd大小的bool数组。 如果给trie树维护计数器,那么内存又会增加4 * n * d/1024/1024≈123MB。加上之前那些乱七八糟的东西和系统栈,内存十分爆炸,~~1G刚好不够。~~ ~~凉凉?~~ ~~不,强行续命!~~ 我们可以发现这个做法并不需要维护节点的子树size,只需要对叶子结点维护计数器,而叶子节点没有左右儿子,这不是明显可以 一**var**多用。 如果是数组写的trie树,那么题已经做完了。 ~~指针只能凉凉?~~ ~~其实还能再续。~~ 虽然在c++(或者说oi版c++)中,变量是有类型的,但是我们还有强制类型转换!反正都是一段内存,而且指针保存的东西本质上就是个作为偏移量的整数,怎么不能当计数器? (int)p->son[0]=1;//ce 貌似强制类型转换返回的是常量,不能做左值。~~所以还是凉凉?~~ 这个东西其实是可以绕过去的。 我们可以把它写成这样* (int*)(&p->son[0])=1; 这样(int*)(&p->son[0])是一个指向变量的常量指针,指向的变量是(p->son[0])(当做int处理),这不就可以做修改了吗。 然后就可以 ~~写出来ac代码~~ 强行续命80分代码了。 时间复杂度考虑trie树深度应该是O(nd+mlogn+(m-n)d),本题d=64. 跑的奇慢,交之前或许需要洗把脸。 ```cpp // luogu-judger-enable-o2 //01trie #include <cstdio> #include <queue> #include <algorithm> using namespace std; #define reg register typedef long long vt; const int kmaxn = 500000 + 5; const int kmaxd = 65;//64//<< 0-63 const int kmaxs = 62 * 500000; struct node { node* son[2]={nullptr,nullptr}; }; node mempool[kmaxs]; int mpt; node* root[kmaxn]; node* stk[kmaxn][kmaxd]; bool hsh[kmaxn][kmaxd][2]; short st[kmaxn]; int n, k; vt arr[kmaxn]; inline node* alloc_node() { return mpt<kmaxs ? &mempool[mpt++] : new node; } void insert(node* lp, node*& p, vt v, int pos = 62) { p = alloc_node(); if (lp)*p = *lp; if (pos<0) { *(int*)(&(p->son[0])) = *(int*)(&(p->son[0]))+1; //*(int*)(&p->son[0])=1; // cout<<(int)p->son[0]<<endl; return; } bool b = (v)&(((vt)1) << pos); insert((lp ? lp->son[b] : nullptr), p->son[b], v, pos - 1); } vt init(node* p, vt v, int num, int pos = 62) { stk[num][++st[num]] = p; if (pos<0)return 0; bool b = !((v)&(((vt)1) << pos)); if (p->son[b]) { hsh[num][pos][b] = true; return (((vt)1) << pos) + init(p->son[b], v, num, pos - 1); } else { hsh[num][pos][!b] = true; return init(p->son[!b], v, num, pos - 1); } } //priority_queue<pair<vt,int>> q; struct unit { vt v; int cnt, pos; unit(vt _v = 0, int c = 0, int p = 0) :v(_v), cnt(c), pos(p) { }; inline const bool operator<(const unit& u)const { return v<u.v; } }; /*class pq{ public: unit u[kmaxn]; int len=0; inline void push(const unit& v) { u[len++]=v; push_heap(u,u+len); } inline unit pop() { pop_heap(u,u+len); return u[--len]; } }q;*/ priority_queue<unit> q; int dfs(int pos, vt v, vt& ans) { short& tail = st[pos]; reg bool dir = false; reg bool b = false; reg int dp = 0; while (tail>1) { dir = (stk[pos][tail - 1]->son[1] == stk[pos][tail]); dp = 63 - (--tail); b = (v)&(((vt)1) << (dp)); if (b != dir) ans -= (((vt)1) << (dp)); dir = !dir; if (!hsh[pos][dp][dir] && stk[pos][tail]->son[dir])//find { break; } hsh[pos][dp][0] = hsh[pos][dp][1] = 0; } while (tail<64)//9223372032559812379 { dp = 63 - tail; dir = !((v)&(((vt)1) << dp)); if (stk[pos][tail]->son[dir] && !hsh[pos][dp][dir])//have and can turn { ans += (((vt)1) << (dp)); } else { dir = !dir; } hsh[pos][dp][dir] = true; stk[pos][tail + 1] = stk[pos][tail]->son[dir]; ++tail; } return *(int*)(&(stk[pos][tail]->son[0])); } inline void solve() { unsigned long long ans = 0; reg int pos = 0; reg int c = 0; vt t = -1; unit temp; while (k) { // printf("k %d\n", k); //temp=q.pop(); temp = q.top(); q.pop(); t = temp.v; c = min(temp.cnt, k); k -= c; pos = temp.pos; ans += t*c; c = dfs(pos, arr[pos], t); if (t >= 0) q.push(unit(t, c, pos)); t = -1; } printf("%lld\n", ans); } int main() { // freopen("xor19.in", "r", stdin); scanf("%d%d", &n, &k); // printf("%d %d\n", n, k); insert(nullptr, root[0], 0); vt t=0; for (reg int i = 1; i <= n; ++i) { //cin>>arr[i]; scanf("%lld", &arr[i]); arr[i] ^= arr[i - 1]; insert(root[i - 1], root[i], arr[i]); t = init(root[i], arr[i], i); // printf("%d cnt %d\n", i, *(int*)(void*)(&(stk[i][64]->son[0]))); q.push(unit(t, *(int*)(&(stk[i][64]->son[0])), i)); } solve(); // while (1); return 0; } ``` ~~幸好考场上死也没想出来需要维护计数器,否则MLE 80变60就很惨了(80变0更惨)~~