LG4585 [FJOI2015]火星商店问题 题解
LG4585 solution
商店的编号 \(1 \sim n\)。商店里出售的商品中,每种商品都用一个非负整数 \(\text{val}\) 来标价。每个商店每天都有可能进一些新商品,其标价可能与已有商品相同。
火星人每次会训责商店编号在区间 \([l,r]\) 中的商店,从中挑选一件自己最喜欢的商品。每个火星人对商品的喜好标准各不相同。
每个火星人都有一个自己的密码 \(x\)。对每种标价为 \(\text{val}\) 的商品,密码为 \(x\) 的火星人对这种商品的喜好程度与 \(\text{val}\) 异或 \(x\) 的值成正比。也就是说,\(\text{val xor }x\) 的值越大,他就越喜欢该商品。
每个火星人的购物卡在所有商店中只能购买最近 \(d\) 天内(含当天)进货的商品。另外,每个商店都有一种特殊商品不受进货日期限制,每位火星人在任何时刻都可以选择该特殊商品。每个商店中每种商品都能保证供应,不存在商品缺货的问题。
对于给定的按时间顺序排列的事件,计算每个购物的火星人的在本次购物活动中最喜欢的商品,即输出 \(\text{val xor }x\) 的最大值。这里所说的按时间顺序排列的事件是指以下两种事件:
0 s v
,表示编号为 \(s\) 的商店在当日新进一种标价为 \(v\) 的商品。
1 l r x d
,表示一位火星人当日在编号在 \([l,r]\) 的商店购买 \(d\) 天内的商品,该火星人的喜好密码为 \(x\)。
这题琢磨了好久,终于有点懂了题解的线段树分治为什么是线段树分治,但对于这题来说还是线段树套 01trie 在实现上比较简单。
线段树分治
基本思想
我们还是对时间进行线段树分治,只不过我们的查询操作来到了区间上,而修改操作成了单点的。
对于每个在时间 \(t\) 修改操作,我们在线段树的每个包含 \(t\) 的区间都加入这个操作的信息,即位置与值。
对于每个在时间 \([l,r]\) 之间的查询操作,也同样在线段树上的对应区间加入这个查询及其各项信息。
实现
我们对于每个一开始就有并一直会有的特殊商品,可以建一颗可持久化 01trie。对于一个查询操作就是在 trie 上做差分贪心(基础知识),并将每个询问的答案记录下来。最后将这颗跑包含特殊商品的 trie 清空,方便后续操作,以后同样也不许考虑特殊商品的贡献。
遍历加入所有修改与查询操作的线段树,对于一个时间每个线段树上遍历的节点 \(p\),将 \(p\) 上的所有修改操作按时间顺序加入可持久化 01trie 中,同时 01trie 的要记录一下每个根节点的时间。(即每个修改的具体时间,方便后续查询)。
然后查询节点 \(p\) 的所有询问,此时的时间限制自然是满足的,于是我们就在找到询问区间的端点在 01trie 上的根节点(使用二分查找),然后找差分后按位贪心即可,并统计答案是否更优秀。
最后将答案数组输出即可。
这个算法细节还是不少的,这里我偷懒了。洛谷题解区第一篇是实现形似整体二分的线性空间的做法。这里推荐这篇结构比较清晰的题解。
线段树套01trie
题目无非是让我们在线单点修改并区间查询。
所以我们对线段树上的每一个节点开一个 01trie,这颗 01trie 的每个节点还应统计最大的能到达它的时间(贪心,相信读了题就能懂)。然后对于修改操作就改从根到叶子的每个包含它的节点,查询的时候就查每个区间的答案然后取最大即可。
空间复杂度 \(O(n\log^2n)\),不知道有没有问题。这题限 500MiB,最大的点三百多 M。
#define ls p<<1
#define rs p<<1|1
#define mid ((l+r)>>1)
const int N=100005;
int n,m,a[N];
int ch[N<<8][2],tag[N<<8][2],root[N<<2],tot;
inline void insert(int p,int val,int lim){
for(int i=18;i>=0;--i){
int c=((val>>i)&1);
if(!ch[p][c])ch[p][c]=++tot;
tag[p][c]=max(lim,tag[p][c]);
p=ch[p][c];
}
}
inline int query(int p,int val,int lim){
int tmp=0;
for(int i=18;i>=0;--i){
int v=((val>>i)&1);
if(ch[p][v^1]&&tag[p][v^1]>=lim)
{tmp+=(1<<i);p=ch[p][v^1];}
else if(ch[p][v]&&tag[p][v]>=lim)
p=ch[p][v];
}
return tmp;
}
void build(int p,int l,int r){
root[p]=++tot;
for(int i=l;i<=r;++i)
insert(root[p],a[i],N);
if(l==r)return;
build(ls,l,mid);build(rs,mid+1,r);
}
void update(int p,int l,int r,int L,int val,int lim){
insert(root[p],val,lim);
if(l==r)return;
if(L<=mid)update(ls,l,mid,L,val,lim);
else update(rs,mid+1,r,L,val,lim);
}
int query(int p,int l,int r,int L,int R,int val,int lim){
if(L<=l&&r<=R)return query(root[p],val,lim);
int tmp=0;
if(L<=mid)tmp=max(tmp,query(ls,l,mid,L,R,val,lim));
if(R>mid)tmp=max(tmp,query(rs,mid+1,r,L,R,val,lim));
return tmp;
}
int main(){
n=read(),m=read();
for(int i=1;i<=n;++i)
a[i]=read();
build(1,1,n);
for(int T=1,day=0;T<=m;++T){
int opt=read();
if(opt==0){
++day;
int x=read(),y=read();
update(1,1,n,x,y,day);
}else{
int l=read(),r=read(),x=read(),len=read();
printf("%d\n",query(1,1,n,l,r,x,day-len+1));
}
}
return 0;
}
本文作者:BigSmall_En
本文链接:https://www.cnblogs.com/BigSmall-En/p/16579749.html
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步