这道题的每个询问都有两个区间,一个是时间区间,一个是商店编号区间。
每个购买也是和时间商店编号有关。
如何让这两个参数联系起来,就需要用到线段树表示时间区间。线段树可以把时间区间分细。对于每一个询问,它都有一个时间区间
(t为当前询问时间,d为询问中向前延长的时间)都要放到线段树上的节点上的vector中,用线段树上vector保存对应时间为
的询问编号。
解决时,购买先按商店编号排序,以完成询问中第二个区间变量。
线段树分治,参数有now,tl,tr,l,r分别表示线段树端点,购买操作tl,tr的时间是在l,r中,且tl,tr的编号有序。
#include <bits/stdc++.h>
#define ls (now << 1)
#define rs (now << 1 | 1)
#define mid ((l + r) >> 1)
#define pb(x) push_back(x)
#define LL long long
using namespace std;
inline void read(int &x){
x = 0; int f = 1; char ch = getchar();
while (!(ch >= '0' && ch <= '9')){if (ch == '-') f = -1; ch = getchar();}
while (ch >= '0' && ch <= '9'){x = x * 10 + ch - '0'; ch = getchar();}
x *= f;
}
inline void Max(int &x, int y){if (y > x) x = y;}
inline void Min(int &x, int y){if (y < x) x = y;}
const int MAX = 0x7fffffff;
const int MIN = 0x80000000;
const int INF = 0x3f3f3f3f;
const int N = 1e5 + 10;
int n, m, cnt1, cnt2, ans[N], rt[N];
struct Add{
int s, v, t;
Add(int a = 0, int b = 0, int c = 0){s = a, v = b, t = c;}
} q[N], tmp1[N], tmp2[N];
inline bool cmp(Add &a, Add &b){return a.s < b.s;}
struct Ask{
int l, r, tl, tr, x;
Ask(int a = 0, int b = 0, int c = 0, int d = 0, int e = 0){l = a, r = b, tl = c, tr = d, x = e;}
} p[N];
struct Trie{
struct Node{int son[2], w;} T[N << 5];
int tot;
inline void Insert(int &x, int y, int w, int now){
T[x = ++tot] = T[y]; ++T[x].w;
if (now == -1) return;
bool c = w & (1 << now);
Insert(T[x].son[c], T[y].son[c], w, now - 1);
}
inline int query(int x, int y, int w, int now){
if (now == -1) return 0;
bool c = w & (1 << now);
int tmp = T[T[y].son[c ^ 1]].w - T[T[x].son[c ^ 1]].w;
if (tmp) return query(T[x].son[c ^ 1], T[y].son[c ^ 1], w, now - 1) + (1 << now);
else return query(T[x].son[c], T[y].son[c], w, now - 1);
}
}trie;
vector<int> ve[N];
struct Seg{
inline void Modify(int now, int tl, int tr, int x, int l, int r){
if (tl > tr) return;
if (l == tl && r == tr){ve[now].pb(x); return;}
if (tr <= mid) Modify(ls, tl, tr, x, l, mid);
else if (tl > mid) Modify(rs, tl, tr, x, mid + 1, r);
else Modify(ls, tl, mid, x, l, mid), Modify(rs, mid + 1, tr, x, mid + 1, r);
}
int top, st[N];
inline int low(int x){
int l = 1, r = top;
while (l <= r){
if (st[mid] <= x) l = mid + 1;
else r = mid - 1;
}
return r;
}
inline void calc(int now, int tl, int tr){
if (!ve[now].size()) return;
top = trie.tot = 0;
for (int i = tl; i <= tr; i++){
st[++top] = q[i].s;
trie.Insert(rt[top], rt[top - 1], q[i].v, 17);
}
for (int i = 0, sz = ve[now].size(); i < sz; i++){
int k = ve[now][i];
int l = low(p[k].l - 1), r = low(p[k].r);
Max(ans[k], trie.query(rt[l], rt[r], p[k].x, 17));
}
}
//divide(线段树上节点编号,[tl,tr]表示被时间[l,r]分成的q数组上的购买时间)
//相当于是对每一个购买时间进行分治,并用线段树上的节点的vector来表示查询问题
//线段树作用只是用来保存查询问题,使购买和查询拥有共同的时间段
inline void divide(int now, int tl, int tr, int l, int r){
if (tl > tr) return;
int t1 = 0, t2 = 0;
calc(now, tl, tr); if (l == r) return;
for (int i = tl; i <= tr; i++)
if (q[i].t <= mid) tmp1[++t1] = q[i];
else tmp2[++t2] = q[i];
for (int i = 1; i <= t1; i++) q[i + tl - 1] = tmp1[i];
for (int i = 1; i <= t2; i++) q[i + tl - 1 + t1] = tmp2[i];
divide(ls, tl, tl + t1 - 1, l, mid);
divide(rs, tl + t1, tr, mid + 1, r);
}
}seg;
int main(){
read(n), read(m);
for (int i = 1; i <= n; i++){
int x; read(x);
trie.Insert(rt[i], rt[i - 1], x, 17);
}
for (int i = 1; i <= m; i++){
int opt; read(opt);
if (!opt){
int s, v; read(s), read(v); ++cnt1;
q[cnt1] = Add(s, v, cnt1);
}else{
int l, r, x, d; read(l), read(r), read(x), read(d);
ans[++cnt2] = trie.query(rt[l - 1], rt[r], x, 17);
p[cnt2] = Ask(l, r, max(1, cnt1 - d + 1), cnt1, x);
}
}
for (int i = 1; i <= cnt2; i++)
seg.Modify(1, p[i].tl, p[i].tr, i, 1, cnt1);
sort(q + 1, q + 1 + cnt1, cmp);
seg.divide(1, 1, cnt1, 1, cnt1);
for (int i = 1; i <= cnt2; i++) printf("%d\n", ans[i]);
return 0;
}