题目链接:火星商店问题
感觉就是HDU 5390 的加强版
询问是一段时间段,而更新是时间点
固定商品就是一棵可持久化 trie上区间查找。考虑新购买的商品:
在线做法:以时间建一棵线段树,每个节点维护一个可持久化 trie,通过线段树区间查询各个时间段来求得询问的答案。(也就是线段树套可持久化 trie 的做法)这么做空间必定裂开。
考虑离线做法:询问可以在线段树上分成多个子区间且互不干扰(也就是子问题),考虑在线段树上
离线分治:将询问操作的时间区间拆成 log 段,插入到对应的线段树上的结点中,更新操作的时间是一个点,从根节点一路插入到叶子结点。
遍历线段树的所有结点,对于线段树每个结点的操作序列:将更新操作按下标排序,依次维护到可持久化 trie 上,询问操作的下标离散化,在 当前维护的可持久化 trie 上查询即可。
代码:
#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e5 + 10; //在洛谷上空间得开大点
int n,m,val[maxn],ans[maxn];
vector<int> t; //用于离散化
struct trie{ //可持久化字典树
int sz,son[maxn * 40][2],sum[maxn * 40],root[maxn];
void init() {
for(int i = 0; i <= sz; i++)
root[i] = son[i][0] = son[i][1] = sum[i] = 0;
sz = 0;
}
void upd(int &rt,int v) { //类似主席树的更新,每一步都要更新一个结点
++sz;
son[sz][0] = son[rt][0];
son[sz][1] = son[rt][1];
sum[sz] = sum[rt] + 1;
rt = sz;
int t = rt;
for(int i = 19; i >= 0; i--) {
int p = (v >> i) & 1;
++sz;
int s = son[t][p];
sum[sz] = sum[s] + 1;
son[sz][0] = son[s][0];
son[sz][1] = son[s][1];
son[t][p] = sz;
t = son[t][p];
}
}
int qry(int l,int r,int v) {
if(l > r) return 0;
int ans = 0;
int L = root[l],R = root[r];
for(int i = 19; i >= 0; i--) {
int p = (v >> i) & 1;
if(sum[son[R][p ^ 1]] - sum[son[L][p ^ 1]] > 0) {
L = son[L][p ^ 1],R = son[R][p ^ 1];
ans |= (1 << i);
} else {
L = son[L][p],R = son[R][p];
}
}
return ans;
}
}tr1,tr2;
struct node {
int id,op,a,b,v;
node(int id = 0,int op = 0,int a = 0,int b = 0,int v = 0) {
this -> id = id;
this -> op = op;
this -> a = a;
this -> b = b;
this -> v = v;
}
bool operator < (const node &rhs) const {
if(op == rhs.op && op == 0) return a < rhs.a;
return op < rhs.op;
}
};
struct seg_tree {
vector<node> opt[maxn << 2];
#define lson rt << 1,l,mid
#define rson rt << 1 | 1,mid + 1,r
void build(int rt,int l,int r) {
opt[rt].clear();
if(l == r) return ;
int mid = l + r >> 1;
build(lson);build(rson);
}
void insert_upd(int p,node v,int rt,int l,int r) {
opt[rt].push_back(v);
if(l == r) return ;
int mid = l + r >> 1;
if(p <= mid) insert_upd(p,v,lson);
else insert_upd(p,v,rson);
}
void insert_qry(int L,int R,node v,int rt,int l,int r) {
if(L > R) return ;
if(L <= l && r <= R) {
opt[rt].push_back(v);
return ;
}
int mid = l + r >> 1;
if(L <= mid) insert_qry(L,R,v,lson);
if(mid + 1 <= R) insert_qry(L,R,v,rson);
}
void solve(int rt,int l,int r) {
t.clear(); //离散化
sort(opt[rt].begin(),opt[rt].end());
for(auto it : opt[rt]) {
if(it.op == 0)
t.push_back(it.a);
}
int p = unique(t.begin(),t.end()) - t.begin();
tr2.init();
for(auto it : opt[rt]) {
if(it.op == 0) {
int tmp = lower_bound(t.begin(),t.begin() + p,it.a) - t.begin() + 1;
if(!tr2.root[tmp])
tr2.root[tmp] = tr2.root[tmp - 1];
tr2.upd(tr2.root[tmp],it.b);
} else {
int li = lower_bound(t.begin(),t.begin() + p,it.a) - t.begin() + 1;
int ri = upper_bound(t.begin(),t.begin() + p,it.b) - t.begin();
ans[it.id] = max(ans[it.id],tr2.qry(li - 1,ri,it.v));
}
}
if(l == r) return ;
int mid = l + r >> 1;
solve(lson); solve(rson);
}
}seg;
int main() {
scanf("%d%d",&n,&m);
tr1.init();
vector<int> tmp;
for(int i = 1; i <= n; i++) {
scanf("%d",&val[i]);
tr1.root[i] = tr1.root[i - 1];
tr1.upd(tr1.root[i],val[i]);
}
seg.build(1,1,m);
int tot = 0;
for(int i = 1; i <= m; i++) {
int op,a,b,v,d;
scanf("%d",&op);
if(op == 0) {
scanf("%d%d",&a,&b);
seg.insert_upd(++tot,node(i,0,a,b,0),1,1,m);
} else {
scanf("%d%d%d%d",&a,&b,&v,&d);
ans[i] = tr1.qry(a - 1,b,v);
seg.insert_qry(max(tot - d + 1,1),tot,node(i,1,a,b,v),1,1,m);
tmp.push_back(i);
}
}
seg.solve(1,1,m);
for(auto it : tmp)
printf("%d\n",ans[it]);
return 0;
}