题目链接:点击查看
题目大意:初始时给出 n 个数字组成的数列,接下来有 m 次操作,分别对应着区间查询最大值以及单点修改,实现 m 次操作
题目分析:线段树裸题,就不多说了,Splay的话,就在每个节点再维护一个 val 和一个 mmax 分别代表点权和子树的最大值,单点修改很简单,只需要找到对应的节点修改权值就好了,而区间查询也非常的巧妙,首先将 l - 1 这个节点旋到根节点处,这样根节点的右子树中的所有节点的下标都一定比 l - 1 大了,此时再把 r + 1 这个节点旋到根节点的右儿子处,根据SBT的性质可知,此时根节点的右儿子的左子树中的所有节点一定都属于 [ l , r ] 这个区间中了,因为 mmax 维护的是子树中的最大值,所以答案就是根节点的右儿子的左儿子的 mmax 了
非常巧妙的思想,利用Slpay实现了区间查询的功能,时间复杂度均摊下来也是 logn ,不过唯一的要求就是需要自己动手对模板内部实现进行一定量的修改,从而满足自己的需求,这就需要对模板的理解有着一定程度的要求了
代码:
#include<iostream>
#include<cstdio>
#include<string>
#include<ctime>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<stack>
#include<climits>
#include<queue>
#include<map>
#include<set>
#include<sstream>
using namespace std;
typedef long long LL;
typedef unsigned long long ull;
const int inf=0x3f3f3f3f;
const int N=2e5+100;
class Splay//存储规则:小左大右,重复节点记录
{
#define root e[0].ch[1] //该树的根节点
private:
class node
{
public:
int mmax;
int val;
int v,father;//节点值,父级节点
int ch[2];//左孩子=0,右孩子=1
int sum;//自己+自己下级有多少节点。在根节点为1。
int recy;//记录自己被重复了几次
};
node e[N];//Splay树主体
int n,points;//使用存储数,元素数
void update(int x)
{
e[x].sum=e[e[x].ch[0]].sum+e[e[x].ch[1]].sum+e[x].recy;
e[x].mmax=max(max(e[e[x].ch[0]].mmax,e[e[x].ch[1]].mmax),e[x].val);
}
int identify(int x)
{
return e[e[x].father].ch[0]==x?0:1;
}
void connect(int x,int f,int son)//连接函数。用法:connect(son,father,1/0)
{
e[x].father=f;
e[f].ch[son]=x;
}//作用:使得x的father=f,f的son=x.
void rotate(int x)
{
int y=e[x].father;
int mroot=e[y].father;
int mrootson=identify(y);
int yson=identify(x);
int B=e[x].ch[yson^1];
connect(B,y,yson);connect(y,x,(yson^1));connect(x,mroot,mrootson);
update(y);update(x);
}
void splay(int at,int to)//将at位置的节点移动到to位置
{
to=e[to].father;
while(e[at].father!=to)
{
int up=e[at].father;
if(e[up].father==to) rotate(at);
else if(identify(up)==identify(at))
{
rotate(up);
rotate(at);
}
else
{
rotate(at);
rotate(at);
}
}
}
int crepoint(int v,int father,int num)
{
n++;
e[n].v=v;
e[n].father=father;
e[n].sum=e[n].recy=1;
e[n].val=num;
e[n].mmax=num;
e[n].ch[0]=e[n].ch[1]=0;
return n;
}
void destroy(int x)//pop后摧毁节点
{
e[x].v=e[x].ch[0]=e[x].ch[1]=e[x].sum=e[x].father=e[x].recy=0;
if(x==n) n--;//最大限度优化
}
public:
void init()
{
points=n=root=0;
e[root].v=e[root].father=e[root].sum=e[root].recy=e[root].val=e[root].mmax=e[root].ch[0]=e[root].ch[1]=0;
}
int getroot(){return root;}
int find(int v)//用于外部的find调用
{
int now=root;
while(true)
{
if(e[now].v==v)
{
// splay(now,root);
return now;
}
int next=v<e[now].v?0:1;
if(!e[now].ch[next]) return 0;
now=e[now].ch[next];
}
}
int build(int v,int num)//内部调用的插入函数,没有splay
{
points++;
if(points==1)//特判无点状态
{
root=n+1;
crepoint(v,0,num);
}
else
{
int now=root;
while(true)//向下找到一个空节点
{
e[now].sum++;//自己的下级肯定增加了一个节点
if(v==e[now].v)
{
e[now].recy++;
return now;
}
int next=v<e[now].v?0:1;
if(!e[now].ch[next])
{
crepoint(v,now,num);
e[now].ch[next]=n;
return n;
}
now=e[now].ch[next];
}
}
return 0;
}
void push(int v,int num)//插入元素时,先添加节点,再进行伸展
{
int add=build(v,num);
splay(add,root);
}
void pop(int v)//删除节点
{
int deal=find(v);
if(!deal) return;
points--;
if(e[deal].recy>1)
{
e[deal].recy--;
e[deal].sum--;
return;
}
if(!e[deal].ch[0])
{
root=e[deal].ch[1];
e[root].father=0;
}
else
{
int lef=e[deal].ch[0];
while(e[lef].ch[1]) lef=e[lef].ch[1];
splay(lef,e[deal].ch[0]);
int rig=e[deal].ch[1];
connect(rig,lef,1);connect(lef,0,1);
update(lef);
}
destroy(deal);
}
int rank(int v)//获取值为v的元素在这棵树里是第几小
{
int ans=0,now=root;
while(true)
{
if(e[now].v==v)
{
ans+=e[e[now].ch[0]].sum;
splay(now,root);
return ans+1;
}
if(now==0) return 0;
if(v<e[now].v) now=e[now].ch[0];
else
{
ans=ans+e[e[now].ch[0]].sum+e[now].recy;
now=e[now].ch[1];
}
}
return 0;
}
int atrank(int x)//获取第x小的元素的值
{
if(x>points) return -inf;
int now=root;
while(true)
{
int minused=e[now].sum-e[e[now].ch[1]].sum;
if(x>e[e[now].ch[0]].sum&&x<=minused) break;
if(x<minused) now=e[now].ch[0];
else
{
x=x-minused;
now=e[now].ch[1];
}
}
splay(now,root);
return e[now].v;
}
int upper(int v)//寻找该值对应的一个最近的上界值
{
int now=root;
int result=inf;
while(now)
{
if(e[now].v>=v&&e[now].v<result) result=e[now].v;
if(v<e[now].v) now=e[now].ch[0];
else now=e[now].ch[1];
}
return result;
}
int lower(int v)//寻找该值对应的一个最近的下界值
{
int now=root;
int result=-inf;
while(now)
{
if(e[now].v<=v&&e[now].v>result) result=e[now].v;
if(v>e[now].v) now=e[now].ch[1];
else now=e[now].ch[0];
}
return result;
}
void UPDATE(int pos,int val)
{
splay(find(pos),root);//旋到根
e[root].val=val;
e[root].mmax=max(max(e[e[root].ch[0]].mmax,e[e[root].ch[1]].mmax),val);
}
int query(int l,int r)
{
splay(find(l-1),root);
splay(find(r+1),e[root].ch[1]);
return e[e[e[root].ch[1]].ch[0]].mmax;
}
#undef root
}tree;
int main()
{
#ifndef ONLINE_JUDGE
// freopen("input.txt","r",stdin);
// freopen("output.txt","w",stdout);
#endif
// ios::sync_with_stdio(false);
int n,m;
while(scanf("%d%d",&n,&m)!=EOF)
{
tree.init();
tree.push(0,-inf);
for(int i=1;i<=n;i++)
{
int num;
scanf("%d",&num);
tree.push(i,num);
}
tree.push(n+1,-inf);
while(m--)
{
char op[5];
scanf("%s",op);
if(op[0]=='U')
{
int pos,val;
scanf("%d%d",&pos,&val);
tree.UPDATE(pos,val);
}
else
{
int l,r;
scanf("%d%d",&l,&r);
printf("%d\n",tree.query(l,r));
}
}
}
return 0;
}