前置知识
-
线段树
-
可持久化01trie
第一道线段树分治题
线段树分治是一种按时间分治的方法
由于其结构类似线段树,被称为线段树分治
题意坑点
下面这个讨论里叙述得很清楚了,这里不再赘述
线段树分治核心思想
将询问与修改离线
可能有这几种情况
-
单点修改,单点查询
这个完全不需要线段树分治吧
-
单点修改,区间查询(例如本题)
我们可以将每一个修改记录在包含该点的线段树上的个节点上
把查询分解成个线段树上的区间记录在这个节点上
由于查询的区间包括了我们记录下的每一个小区间
而在每一个线段树区间记录下的修改都会影响这个区间
正确性显然
那么我们遍历一边整棵线段树,在每个节点进行该结点所记录的修改,再进行该结点的询问即可
-
区间修改,单点查询
将每一个修改分解成个区间记录在线段树这个节点上
将每一个查询记录在包含该点的线段树上的个节点上
遍历线段树类比情况2即可
-
区间修改,区间查询
结合情况2,3就是这个东西
我们还需注意在所有中,应该是进行完了修改就直接查询
情况2中应在未递归时便撤销所有修改
本题做法
我们把答案分两个部分来求
第一部分
先用可持久化trie解决所有商店的特殊商品对询问的贡献
这一部分不清楚的可以去学习下最大异或和
第二部分
由于每个询问只能买某一段时间内的商品
暴力的对所有区间做的时间复杂度可达,为值域
我们考虑刚刚学到的线段树分治
将所有修改和询问按照上述的做法记录在线段树上
进行线段树分治
inline void solve(register int k,register int l,register int r){
Insert(k);//将所有修改加进可持久化01trie中
if(l==r){
for(register int i=0;i<que[k].size();i++){
register int id=que[k][i].id;
register int x=que[k][i].l,y=que[k][i].r,val=que[k][i].x;
ans[id]=max(ans[id],query(x,y,val));
}
//处理询问
Delete(k);//清空可持久化01trie
return ;
}
register int mid=l+((r-l)>>1);
for(register int i=0;i<que[k].size();i++){
register int id=que[k][i].id;
register int x=que[k][i].l,y=que[k][i].r,val=que[k][i].x;
ans[id]=max(ans[id],query(x,y,val));
}
Delete(k);
//一定要先清空,这样做的原因应该比较显然
solve(k<<1,l,mid);
solve(k<<1|1,mid+1,r);
return ;
}
这里处理可持久化01trie时需要用到一些叫常用的技巧
由于的序列商店编号不连续,我们需要离散化
在query时需要二分一下
复杂度分析
根据上述的线段树分治需要记录的东西很容易看出空间复杂度
由于我们会遍历所有记录的询问和查询共个
无论是可持久化01trie的插入还是询问时间复杂度均为
故总复杂度
代码
#include <bits/stdc++.h>
using namespace std;
const int maxn=1000005,maxm=100005;
struct Upd{
int s,v;
bool operator <(Upd i)const{
return s<i.s;
}
};
struct Que{
int id,l,r,x;
};
int ans[maxm];
struct trie{
int s[2];
int b;
}t[20*maxm];
int tot=0;
int rt[maxn];
int lp[maxn],rp[maxn];
int W=0;
int shop[maxn];
bool vis[maxm];
vector<Upd>upd[maxm<<2];
vector<Que>que[maxm<<2];
inline int read(){
register int x=0,y=1;
register char ch=getchar();
while(ch<48||ch>57){if(ch==45)y=-1;ch=getchar();}
while(ch>=48&&ch<=57)x=(x<<3)+(x<<1)+(ch^48),ch=getchar();
return x*y;
}
inline void write(register int x){
if(!x)return ;
write(x/10);
putchar(x%10+'0');
return ;
}
inline void clear(register int k){
t[k].s[0]=t[k].s[1]=t[k].b=0;
return ;
}
inline int newnode(){
clear(tot+1);
return ++tot;
}
inline void ins(register int x,register int y,register int val){
if(!rt[x])rt[x]=newnode();
register int k=rt[x],l=rt[y];
t[k].b=x;
for(register int i=16;i>=0;i--){
register bool to=((val&(1<<i))>>i);
if(!t[k].s[to])t[k].s[to]=newnode();
t[k].s[to^1]=t[l].s[to^1];
k=t[k].s[to];
t[k].b=x;
l=t[l].s[to];
}
return ;
}
inline int search(register int x){
register int l=0,r=W+1;
while(l+1<r){
register int mid=l+((r-l)>>1);
if(shop[mid]<=x)
l=mid;
else
r=mid;
}
return l;
}
inline int query(register int l,register int r,register int val){
register int lft=l;
l=search(l);
r=search(r);
if(shop[l]<lft)l++;
l=lp[l];r=rp[r];
register int k=rt[r],ans=0;
for(register int i=16;i>=0;i--){
register bool to=((((val&(1<<i)))>>i)^1);
if(t[k].s[to]==0||t[t[k].s[to]].b<l)to^=1;
else ans+=(1<<i);
k=t[k].s[to];
}
return ans;
}
inline void modify1(register int k,register int l,register int r,register int x,register int p,register int val){
if(l>x||r<x)return ;
if(l==r){
upd[k].push_back((Upd){p,val});
return ;
}
register int mid=l+((r-l)>>1);
modify1(k<<1,l,mid,x,p,val);
modify1(k<<1|1,mid+1,r,x,p,val);
upd[k].push_back((Upd){p,val});
return ;
}
inline void modify2(register int k,register int l,register int r,register int x,register int y,register int id,register int p,register int q,register int val){
if(y<x)return ;
if(l>y||r<x)return ;
if(l>=x&&r<=y){
que[k].push_back((Que){id,p,q,val});
return ;
}
register int mid=l+((r-l)>>1);
modify2(k<<1,l,mid,x,y,id,p,q,val);
modify2(k<<1|1,mid+1,r,x,y,id,p,q,val);
return ;
}
inline void Insert(register int k){
sort(upd[k].begin(),upd[k].end());
if(upd[k].size()){
ins(1,0,upd[k][0].v);
W=1;
shop[W]=upd[k][0].s;
lp[W]=1;
}
for(register int i=1;i<upd[k].size();i++){
register int p1=upd[k][i].s,p2=upd[k][i-1].s;
ins(i+1,i,upd[k][i].v);
if(p1>p2){
W++;
shop[W]=p1;
rp[W-1]=i;lp[W]=i+1;
}
}
rp[W]=upd[k].size();
return ;
}
inline void Delete(register int k){
for(register int i=1;i<=tot;i++)clear(i);
for(register int i=1;i<=upd[k].size();i++)rt[i]=0;
tot=0;
return ;
}
inline void solve(register int k,register int l,register int r){
Insert(k);
if(l==r){
for(register int i=0;i<que[k].size();i++){
register int id=que[k][i].id;
register int x=que[k][i].l,y=que[k][i].r,val=que[k][i].x;
ans[id]=max(ans[id],query(x,y,val));
}
Delete(k);
return ;
}
register int mid=l+((r-l)>>1);
for(register int i=0;i<que[k].size();i++){
register int id=que[k][i].id;
register int x=que[k][i].l,y=que[k][i].r,val=que[k][i].x;
ans[id]=max(ans[id],query(x,y,val));
}
Delete(k);
solve(k<<1,l,mid);
solve(k<<1|1,mid+1,r);
return ;
}
int main(){
freopen("[FJOI2015]火星商店问题.in","r",stdin);
freopen("[FJOI2015]火星商店问题.out","w",stdout);
register int n,m;
n=read();m=read();
clear(0);
for(register int i=1;i<=n;i++){
W++;
shop[W]=i;
lp[W]=i;rp[W]=i;
ins(i,i-1,read());
}
register int day=1;
for(register int i=1;i<=m;i++){
register int opt,l,r,x,d;
opt=read();l=read();r=read();
if(!opt){
if(i>1)day++;
modify1(1,1,m,day,l,r);
}
else{
vis[i]=1;
x=read();d=read();
modify2(1,1,m,max(day-d+1,1),day,i,l,r,x);
ans[i]=query(l,r,x);
}
}
for(register int i=1;i<=tot;i++)clear(i);
for(register int i=1;i<=n;i++)rt[i]=0;
tot=0;
solve(1,1,m);
for(register int i=1;i<=m;i++)
if(vis[i]){
if(ans[i])write(ans[i]);
else putchar('0');
putchar('\n');
}
return 0;
}