Gushing over Progr|

BigSmall_En

园龄:3年2个月粉丝:3关注:5

2022-08-12 14:14阅读: 40评论: 0推荐: 0

LG4585 [FJOI2015]火星商店问题 题解

LG4585 solution

link

商店的编号 \(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 中国大陆许可协议进行许可。

posted @   BigSmall_En  阅读(40)  评论(0编辑  收藏  举报
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
收起