好几天之前做的题目了,一直想写一下博客也没腾出时间来,今天赶紧把坑给填上呼呼呼~
这道题首先如果只考虑每个商店中没有时间限制的物品时,我们只需要使用一棵可持久化trie树来维护区间内的异或最大值即可,这样我们可以把两部分的问题分离开来。
之后我们再考虑有时间限制与编号限制的情况下,该怎样做?无脑做法线段树套trie,直接在对应的区间trie树上贪心,如果该条边的最后更新时间在允许的范围内,说明可以走这条边。虽然这样也可以卡过去(主要在于卡空间),但我们来介绍一种更加妙妙的线段树分治做法。其实我感觉在这题的体现上就是离线版树套树?机房大佬貌似不认可,但我确实是这样觉得哒。
我们考虑在线椴树上的一次区间查询,实际上是将查询的区间分成了log个已经处理好的小区间,分别查询之后再合并达到最终的答案。那么我们可以模拟这个在线段树上分裂区间的操作:
1.如果当前处理的区间(l,r)完全包含于询问区间,我们将这个询问的区间在这一层进行处理。(这样的询问均不再下传,跳过后续步骤)
2.如果询问的区间有涉及(l,mid)的范围,递归左区间处理。
3.如果询问的区间有涉及(mid+1,r)的范围,递归右区间处理。
(本题分治的区间表示时间的范围,可持久化trie负责处理编号的限制)。
这样的复杂度是多少?我们可以分处理trie树与查询的两个方面来分析。处理trie树时,我们插入一个值是log的复杂度,而包含一个修改的总分治区间数也是log个,所以是\(O(nlog^{2}n)\)的复杂度。查询也是log的复杂度,一个查询最多被分别查询 log 次。所以复杂度依然是 \(O(log^{2}n)\) 的。
#include <bits/stdc++.h> using namespace std; #define maxn 1000000 int n, m, sz, qt, nt, tot, cnt, timer; int ans[maxn], sum[maxn * 5], id[maxn], q[maxn]; int bit[30], ch[maxn * 5][2], num[maxn], root[maxn]; int read() { int x = 0, k = 1; char c; c = getchar(); while(c < ‘0‘ || c > ‘9‘) { if(c == ‘-‘) k = -1; c = getchar(); } while(c >= ‘0‘ && c <= ‘9‘) x = x * 10 + c - ‘0‘, c = getchar(); return x * k; } struct ques { int l, r, s, t, x; }Q[maxn]; struct modify { int id, val, t; friend bool operator <(const modify& a, const modify& b) { return a.id < b.id; } }P[maxn], ql[maxn], qr[maxn]; void Ins(int now, int last, int x, int val) { if(x < 0) return; bool t = (bit[x] & val); ch[now][!t] = ch[last][!t]; ch[now][t] = ++ sz; sum[ch[now][t]] = sum[ch[last][t]] + 1; Ins(ch[now][t], ch[last][t], x - 1, val); } int Query(int l, int r, int x, int val) { if(l > r || x < 0) return 0; int ans = 0; bool t = val & bit[x]; if(sum[ch[r][!t]] - sum[ch[l][!t]]) ans = (bit[x] + Query(ch[l][!t], ch[r][!t], x - 1, val)); else ans = Query(ch[l][t], ch[r][t], x - 1, val); return ans; } int Lower(int v) { int l = 1, r = nt, ret = 0; while(l <= r) { int mid = (l + r) >> 1; if(num[mid] <= v) ret = mid, l = mid + 1; else r = mid - 1; } return ret; } void Work(int l, int r) { sz = 0, nt = 0; for(int i = l; i <= r; i ++) { nt ++; root[nt] = ++ sz; Ins(root[nt], root[nt - 1], 17, P[i].val); num[nt] = P[i].id; } for(int i = 1; i <= qt; i ++) { int l = Lower(Q[q[i]].l - 1), r = Lower(Q[q[i]].r); ans[q[i]] = max(ans[q[i]], Query(root[l], root[r], 17, Q[q[i]].x)); } } void Divide(int l, int r, int L, int R, int pres) { if(l > r || !pres) return; int mid = (L + R) >> 1; qt = 0; for(int i = 1; i <= pres; i ++) if(Q[id[i]].s <= L && R <= Q[id[i]].t) q[++ qt] = id[i]; Work(l, r); if(L == R) return; int ll = 0, rr = 0; for(int i = l; i <= r; i ++) { if(P[i].t <= mid) ql[++ ll] = P[i]; else qr[++ rr] = P[i]; } for(int i = 1; i <= ll; i ++) P[i + l - 1] = ql[i]; for(int i = 1; i <= rr; i ++) P[i + l + ll - 1] = qr[i]; int ind = 0; for(int i = 1; i <= pres; i ++) { if(Q[id[i]].s <= L && R <= Q[id[i]].t) continue; if(Q[id[i]].s <= mid) swap(id[i], id[++ ind]); } Divide(l, l + ll - 1, L, mid, ind); ind = 0; for(int i = 1; i <= pres; i ++) { if(Q[id[i]].s <= L && R <= Q[id[i]].t) continue; if(Q[id[i]].t > mid) swap(id[i], id[++ ind]); } Divide(l + ll, r, mid + 1, R, ind); } int main() { bit[0] = 1; for(int i = 1; i <= 20; i ++) bit[i] = bit[i - 1] << 1; n = read(), m = read(); for(int i = 1; i <= n; i ++) root[i] = ++ sz, Ins(root[i], root[i - 1], 17, read()); for(int i = 1; i <= m; i ++) { int opt = read(); if(!opt) { ++ timer; P[++ tot].id = read(); P[tot].val = read(); P[tot].t = timer; } else { Q[++ cnt].l = read(); Q[cnt].r = read(); Q[cnt].x = read(); int d = read(); Q[cnt].s = max(timer - d, 0) + 1; Q[cnt].t = timer; ans[cnt] = Query(root[Q[cnt].l - 1], root[Q[cnt].r], 17, Q[cnt].x); } } sort(P + 1, P + 1 + tot); for(int i = 1; i <= cnt; i ++) id[i] = i; Divide(1, tot, 1, timer, cnt); for(int i = 1; i <= cnt; i ++) printf("%d\n", ans[i]); return 0; }
原文地址:https://www.cnblogs.com/twilight-sx/p/9827600.html