标签:turn 注意 amp 应该 com 线段 bool 为我 www.
最近计划智力康复qwq(话说这题一年前刚刚开始写树套树的时候感觉好难啊qwq现在看其实还好也算是有进步的嘛!)
比较重要的一步是,要将“在\([l,r]\)中只出现一次”这个条件转化成"\(nxt[x]>r\)&&\(pre[x]<l\)",其中\(nxt[x]\)表示下一个出现位置\(x\)的数的位置,\(pre[x]\)表示前一个
然后我们就发现其实是有三个限制:
1、\(pre[x]<l\)
2、\(nxt[x]>r\)
3、最大
那所以我们用两棵主席树套在一起就好了
? 外层的主席树按照\(pre[x]\)来顺序加点,树内按照\(nxt\)的顺序为关键字,然后对于每个节点(也就是区间啦),再开一棵主席树,以\(nxt\)为根的顺序,树内以\(val[x]\)为关键字(也就是权值线段树啦)
? 然后修改直接修改,查询的时候我们先二分一下外层主席树应该调用哪个\(rt\),也就是二分一个最大的\(pos\)满足\(pre[pos]<l\),然后\(query(rt[pos],l,r)\)即可
想清楚了的话还是比较好写的
有一些需要稍微注意一下的小细节
1、如果说对于那些后面已经没有与其相同的数的位置,为了方便我们将其\(nxt\)赋成\(n+1\),所以这里要注意主席树的区间应该是\([1,n+1]\)
2、查询的时候,要注意因为我们要求的是\(nxt[x]>r\),所以在查询中涉及到的范围应该是\(>\)而不是\(>=\)之类的,跟平时写线段树有那么一点点小区别稍微注意一下
代码大概长这个样子
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=100010,M=200010,SEG=N*20;
struct Data{
int pre,nxt,id,val;
friend bool operator < (Data x,Data y)
{return x.pre<y.pre;}
}a[N];
int loc[N];
int n,m,ans;
namespace NodeSeg{/*{{{*/
int ch[SEG*20][2],mx[SEG*20],rt[SEG];
int n,tot;
int newnode(int pre){
ch[++tot][0]=ch[pre][0],ch[tot][1]=ch[pre][1]; mx[tot]=mx[pre];
return tot;
}
void pushup(int x){mx[x]=max(mx[ch[x][0]],mx[ch[x][1]]);}
void _update(int pre,int &x,int lx,int rx,Data &delta){
x=newnode(pre);
if (lx==rx){mx[x]=delta.val;return;}
int mid=lx+rx>>1;
if (delta.id<=mid) _update(ch[pre][0],ch[x][0],lx,mid,delta);
else _update(ch[pre][1],ch[x][1],mid+1,rx,delta);
pushup(x);
}
void update(int pre,int x,Data delta){_update(rt[pre],rt[x],1,n,delta);}
int _query(int x,int l,int r,int lx,int rx){
if (mx[x]==0) return 0;
if (l<=lx&&rx<=r) return mx[x];
int mid=lx+rx>>1,ret=0;
if (l<=mid) ret=max(ret,_query(ch[x][0],l,r,lx,mid));
if (r>mid) ret=max(ret,_query(ch[x][1],l,r,mid+1,rx));
return ret;
}
int query(int x,int l,int r){return _query(rt[x],l,r,1,n);}
}/*}}}*/
namespace NextSeg{/*{{{*/
int ch[SEG][2],rt[SEG];
int n,tot;
int newnode(int pre,Data delta){
ch[++tot][0]=ch[pre][0]; ch[tot][1]=ch[pre][1];
NodeSeg::update(pre,tot,delta);
return tot;
}
void _update(int pre,int &x,int lx,int rx,Data delta){
x=newnode(pre,delta);
if (lx==rx) return;
int mid=lx+rx>>1;
if (delta.nxt<=mid) _update(ch[pre][0],ch[x][0],lx,mid,delta);
else _update(ch[pre][1],ch[x][1],mid+1,rx,delta);
}
void update(int x,Data delta){_update(rt[x-1],rt[x],1,n,delta);}
int _query(int x,int l,int r,int lx,int rx){
if (r<lx&&rx<=n) return NodeSeg::query(x,l,r);
int mid=lx+rx>>1,ret=0;
if (r<mid) ret=max(ret,_query(ch[x][0],l,r,lx,mid));
ret=max(ret,_query(ch[x][1],l,r,mid+1,rx));
return ret;
}
int query(int x,int l,int r){return _query(rt[x],l,r,1,n);}
}/*}}}*/
int read(){
int ret=0; char ch=getchar();
while (ch<'0'||ch>'9') ch=getchar();
while ('0'<=ch&&ch<='9') ret=ret*10+ch-'0',ch=getchar();
return ret;
}
void prework(){
for (int i=1;i<=n;++i) a[i].pre=0,a[i].nxt=n+1;
for (int i=1;i<=n;++i){
a[i].id=i;
if (loc[a[i].val])
a[loc[a[i].val]].nxt=i,a[i].pre=loc[a[i].val];
loc[a[i].val]=i;
}
NodeSeg::n=n+1; NextSeg::n=n+1;
}
int get_pos(int x){
int l=1,r=n,mid,ret=l;
while (l<=r){
mid=l+r>>1;
if (x<=a[mid].pre) r=mid-1;
else l=mid+1;
}
return l-1;
}
int main(){/*{{{*/
#ifndef ONLINE_JUDGE
freopen("a.in","r",stdin);
#endif
int x,y,x1,y1,pos;
n=read(); m=read();
for (int i=1;i<=n;++i) a[i].val=read();
prework();
sort(a+1,a+1+n);
for (int i=1;i<=n;++i)
NextSeg::update(i,a[i]);
for (int i=1;i<=m;++i){
x1=read(); y1=read();
x=min((x1+ans)%n+1,(y1+ans)%n+1);
y=max((x1+ans)%n+1,(y1+ans)%n+1);
pos=get_pos(x);
ans=NextSeg::query(pos,x,y);
printf("%d\n",ans);
}
}/*}}}*/
【bzoj3489】A simple rmq problem
标签:turn 注意 amp 应该 com 线段 bool 为我 www.
原文地址:https://www.cnblogs.com/yoyoball/p/9313850.html