正题
题目链接:https://www.luogu.com.cn/problem/P4585
题目大意
nnn个集合,开始每个集合中有一个数字。
- 开启新的一天并且往集合sss中插入数字vvv
- 询问ddd天以内插入的数字(包括最开始的)中l∼rl\sim rl∼r集合内的数字异或上xxx的最大值。
所有数字均在[0,105][0,10^5][0,105]范围内
解题思路
线段树上每个节点维护一个可持久化TrieTrieTrie。
每次插入就把包含sss的节点插入数字vvv。
询问就正常查询即可。
时空间复杂度都是O(nlog2n)O(n\log^2 n)O(nlog2n)
好像还有空间是O(nlogn)O(n\log n)O(nlogn)的线段树分治算法,不过懒得写了。
code
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=1e5+10;
int n,m,cnt,p[N],rt[N<<2];
int ch[N<<9][2],last[N<<9];
int Insert(int x,int val,int id,int k=16){int y=++cnt;if(k<0){last[y]=id;return y;}int c=(val>>k)&1;ch[y][c^1]=ch[x][c^1];ch[y][c]=Insert(ch[x][c],val,id,k-1);last[y]=max(last[ch[y][0]],last[ch[y][1]]);return y;
}
int Ask(int x,int val,int lim,int k=16){if(k<0)return val;int c=(val>>k)&1;if(last[ch[x][c^1]]>=lim)return Ask(ch[x][c^1],val|(1<<k),lim,k-1);if(last[ch[x][c]]>=lim)return Ask(ch[x][c],val^(val&(1<<k)),lim,k-1);return 0;
}
void Change(int x,int l,int r,int pos,int id,int val){rt[x]=Insert(rt[x],val,id);if(l==r)return;int mid=(l+r)>>1;if(pos<=mid)Change(x*2,l,mid,pos,id,val);else Change(x*2+1,mid+1,r,pos,id,val);return;
}
int Query(int x,int L,int R,int l,int r,int val,int lim){if(L==l&&R==r)return Ask(rt[x],val,lim);int mid=(L+R)>>1;if(r<=mid)return Query(x*2,L,mid,l,r,val,lim);if(l>mid)return Query(x*2+1,mid+1,R,l,r,val,lim);return max(Query(x*2,L,mid,l,mid,val,lim),Query(x*2+1,mid+1,R,mid+1,r,val,lim));
}
int main()
{scanf("%d%d",&n,&m);for(int i=1;i<=n;i++){int x;scanf("%d",&x);p[i]=Insert(p[i-1],x,i);}int id=0;for(int i=1;i<=m;i++){int op;scanf("%d",&op);if(op){int l,r,x,d,ans=0;scanf("%d%d%d%d",&l,&r,&x,&d);ans=Query(1,1,n,l,r,x,id-d+1);ans=max(ans,Ask(p[r],x,l));printf("%d\n",ans);}else{int s,v;scanf("%d%d",&s,&v);id++;Change(1,1,n,s,id,v);}}return 0;
}