题目
题目链接:https://www.luogu.com.cn/problem/P4585
火星上的一条商业街里按照商店的编号 (1 sim n) ,依次排列着 (n) 个商店。商店里出售的琳琅满目的商品中,每种商品都用一个非负整数 ( ext{val}) 来标价。每个商店每天都有可能进一些新商品,其标价可能与已有商品相同。
火星人在这条商业街购物时,通常会逛这条商业街某一段路上的所有商店,譬如说商店编号在区间 ([l,r]) 中的商店,从中挑选一件自己最喜欢的商品。每个火星人对商品的喜好标准各不相同。
通常每个火星人都有一个自己的喜好密码 (x)。对每种标价为 ( ext{val}) 的商品,喜好密码为 (x) 的火星人对这种商品的喜好程度与 ( ext{val}) 异或 (x) 的值成正比。也就是说,( ext{val xor }x) 的值越大,他就越喜欢该商品。
每个火星人的购物卡在所有商店中只能购买最近 (d) 天内(含当天)进货的商品。另外,每个商店都有一种特殊商品不受进货日期限制,每位火星人在任何时刻都可以选择该特殊商品。每个商店中每种商品都能保证供应,不存在商品缺货的问题。
对于给定的按时间顺序排列的事件,计算每个购物的火星人的在本次购物活动中最喜欢的商品,即输出 ( ext{val xor }x) 的最大值。这里所说的按时间顺序排列的事件是指以下两种事件:
0 s v
,表示编号为 (s) 的商店在当日新进一种标价为 (v) 的商品。1 l r x d
,表示一位火星人当日在编号在 ([l,r]) 的商店购买 (d) 天内的商品,该火星人的喜好密码为 (x)。
所有输入均在 ([0,10^5]) 内。
思路
我们发现我们需要解决的问题主要有四维:操作的时间和区间,以及询问的时间及区间。
对询问的时间进行线段树分治,映射到线段树的若干区间内。然后对所有修改按照商店编号从小到大排序,这样就让修改的区间有序了。但是为了在不排序的情况下保证修改的时间,我们可以对修改的时间分治。具体的,每一个分治到的区间由一个五元组 ((x,l,r,ql,qr)) 表示,意思为线段树上的区间编号;区间左右端点;还有对应的操作区间。
然后对于这个区间的操作,因为我们保证了线段树上每一个区间的询问都是包含整棵线段树对应的时间的,所以可以直接进行查询,那么操作的时间也就搞定了。
但是我们需要维护一个数据结构支持区间异或最大值,显然直接上可持久化 Trie 即可。然后询问的区间问题也就迎刃而解。
注意细节。时间复杂度 (O(mlog nlog m))。空间复杂度 (O(mlog n))。
代码
#include <bits/stdc++.h>
using namespace std;
const int N=300010,LG=20;
int n,m,n1,n2,a[N],rt[N*LG],ans[N];
struct Update
{
int s,d,v;
}upd[N],sav[N];
struct Query
{
int l,r,ql,qr,x;
}ask[N];
bool cmp(Update x,Update y)
{
return x.s<y.s;
}
struct Trie
{
int tot,ch[N*LG][2],last[N*LG];
Trie() { tot=rt[0]=1; }
int ins(int dep,int now,int v,int i)
{
int x=++tot,id=(v>>dep)&1;
ch[x][0]=ch[now][0]; ch[x][1]=ch[now][1]; last[x]=i;
if (dep>=0) ch[x][id]=ins(dep-1,ch[now][id],v,i);
return x;
}
int query(int dep,int x,int l,int v)
{
if (dep<0) return last[x];
int id=(v>>dep)&1;
if (ch[x][id^1] && last[ch[x][id^1]]>=l)
return query(dep-1,ch[x][id^1],l,v);
else
return query(dep-1,ch[x][id],l,v);
}
void clr()
{
for (int i=0;i<=tot;i++)
ch[i][0]=ch[i][1]=last[i]=0;
tot=rt[0]=1;
}
}trie;
int binary1(int l,int r,int k)
{
while (l<=r)
{
int mid=(l+r)>>1;
if (upd[mid].s>=k) r=mid-1;
else l=mid+1;
}
return r+1;
}
int binary2(int l,int r,int k)
{
while (l<=r)
{
int mid=(l+r)>>1;
if (upd[mid].s<=k) l=mid+1;
else r=mid-1;
}
return l-1;
}
struct SegTree
{
vector<int> a[N*4];
void update(int x,int l,int r,int ql,int qr,int i)
{
if (ql>qr) return;
if (ql<=l && qr>=r)
{
a[x].push_back(i);
return;
}
int mid=(l+r)>>1;
if (ql<=mid) update(x*2,l,mid,ql,qr,i);
if (qr>mid) update(x*2+1,mid+1,r,ql,qr,i);
}
void solve(int x,int l,int r,int ql,int qr)
{
trie.clr();
for (int i=ql,j=1;i<=qr;i++,j++)
rt[j]=trie.ins(LG,rt[j-1],upd[i].v,j);
for (int i=0;i<a[x].size();i++)
{
int j=a[x][i];
int pl=binary1(ql,qr,ask[j].l)-ql+1;
int pr=binary2(ql,qr,ask[j].r)-ql+1;
if (pl>pr) continue;
int val=upd[trie.query(LG,rt[pr],pl,ask[j].x)+ql-1].v;
ans[j]=max(ans[j],val^ask[j].x);
}
if (l==r) return;
int pl=ql-1,pr=qr+1,mid=(l+r)>>1;
for (int i=ql;i<=qr;i++)
if (upd[i].d<=mid) sav[++pl]=upd[i];
else sav[--pr]=upd[i];
for (int i=ql;i<=pl;i++) upd[i]=sav[i];
for (int i=pr;i<=qr;i++) upd[qr-i+pr]=sav[i];
solve(x*2,l,mid,ql,pl);
solve(x*2+1,mid+1,r,pr,qr);
}
}seg;
int main()
{
// freopen("data.txt","r",stdin);
scanf("%d%d",&n,&m);
for (int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
rt[i]=trie.ins(LG,rt[i-1],a[i],i);
}
for (int i=1;i<=m;i++)
{
int opt,l,r,d,x;
scanf("%d",&opt);
if (!opt)
{
scanf("%d%d",&d,&x);
upd[++n1]=(Update){d,n1,x};
}
else
{
scanf("%d%d%d%d",&l,&r,&x,&d);
ask[++n2]=(Query){l,r,n1-d+1,n1,x};
ans[n2]=a[trie.query(LG,rt[r],l,x)]^x;
}
}
for (int i=1;i<=n2;i++)
seg.update(1,1,n1,ask[i].ql,ask[i].qr,i);
sort(upd+1,upd+1+n1,cmp);
seg.solve(1,1,n1,1,n1);
for (int i=1;i<=n2;i++)
printf("%d
",ans[i]);
return 0;
}