P1198 [JSOI2008]最大数
题目描述
现在请求你维护一个数列,要求提供以下两种操作:
1、 查询操作。
语法:Q L
功能:查询当前数列中末尾L个数中的最大的数,并输出这个数的值。
限制:L不超过当前数列的长度。(L>=0)
2、 插入操作。
语法:A n
功能:将n加上t,其中t是最近一次查询操作的答案(如果还未执行过查询操作,则t=0),并将所得结果对一个固定的常数D取模,将所得答案插入到数列的末尾。
限制:n是整数(可能为负数)并且在长整范围内。
注意:初始时数列是空的,没有一个数。
输入输出格式
输入格式:
第一行两个整数,M和D,其中M表示操作的个数(M <= 200,000),D如上文中所述,满足(0<D<2,000,000,000)
接下来的M行,每行一个字符串,描述一个具体的操作。语法如上文所述。
输出格式:
对于每一个查询操作,你应该按照顺序依次输出结果,每个结果占一行。
输入输出样例
输入样例#1: 复制
5 100
A 96
Q 1
A 97
Q 1
Q 2
输出样例#1: 复制
96
93
96
代码分析:
#include <iostream>
#include <string.h>
#include <stdio.h>
#include <algorithm>
using namespace std;
const int INF=0x3f3f3f3f;
const int mm=200010;
int d;
struct node
{
int fa;
int son[2];
long long val;
int sz;
long long mx;///当前节点及其子节点中的最大值
}tree[mm];
int root,tot1;///tot1当前节点总数
int s[mm],tot2;///因删除而空闲的节点
int n;
long long q;
#define get(x) (tree[tree[x].fa].son[1]==(x))
#define Key_value tree[tree[root].son[1]].son[0]
#define t tree[x]
#define lson tree[x].son[0]
#define rson tree[x].son[1]
#define tlson tree[tree[x].son[0]]
#define trson tree[tree[x].son[1]]
void PushUp(int x)
{
t.sz=tlson.sz+trson.sz+1;
t.mx=max(t.val,tlson.mx);
t.mx=max(t.mx,trson.mx);
}
inline void Rotate(int x)
{
int which=get(x);
int y=tree[x].fa;
int z=tree[y].fa;
tree[y].son[which]=tree[x].son[which^1];
tree[tree[y].son[which]].fa =y;
tree[x].son[which^1]=y;
tree[y].fa=x;
tree[x].fa=z;
if(z!=0)
{
tree[z].son[tree[z].son[1]==y]=x;
}
PushUp(y);
PushUp(x);
}
inline void Splay(int x,int goal)///将当前节点转到目标goal之下,例如goal=0,则表示转为根
{
int fa=tree[x].fa;
while(fa!=goal)
{
if(tree[fa].fa!=goal)
Rotate(get(x)==get(fa)?fa:x);
Rotate(x);
fa=tree[x].fa;
}
if(goal==0)root=x;
}
inline int Get_Kth(int x,int k)///得到当前节点下第k个数据在tree中的位置
{
//PushDown(x);
int tt=tlson.sz+1;
if(tt==k)return x;
if(tt>k)return Get_Kth(lson,k);
else return Get_Kth(rson,k-tt);
}
/***初始化***/
void NewNode(int &x,int fa,long long v)///在节点father下新加一个值为v的节点,x返回该节点在tree数组中的位置
{
if(tot2)x=s[tot2--];
else x=++tot1;
///node s=tree[x];不能
t.fa=fa;
lson=rson=0;
t.val=t.mx=v;
t.sz=1;
}
void Init()
{
root=tot1=tot2=0;
NewNode(root,0, -1);
NewNode(tree[root].son[1],root, -1);
PushUp(root);
}
void Insert_test(long long v)
{
Splay(2,root);
int now=Key_value;
while(tree[now].son[1]!=0)
{
now=tree[now].son[1];
}
NewNode(tree[now].son[1],now,v);
int push=now;
while(push!=0)
{
PushUp(push);
push=tree[push].fa;
}
}/*先是写的这个,没有多想变量之间的关系,但是后来发现其实这个太复杂。可以用tot1(总节点数)直接秒掉这个操作。*/
void Insert(long long v)
{
int x=(tot1==2)?1:tot1;
Splay(x,0);
NewNode(Key_value,2,v);
PushUp(2);
PushUp(root);
}/*我们可以利用tot1,也就是总节点数,因为,我们发现tot1其实就是最后一个的编号(除了开始插入的tree[2]);这比之前写的那个简单多了。。。*/
long long Get_Max(int l)
{
Splay(Get_Kth(root,l-1),0);
Splay(2,root);
return tree[Key_value].mx;
}/*我们把[L,R]之间的数做处时,其实都可以用这种方法:
先把L-1这个节点splay到root。
接着把R+1这个节点splay到tree[root].son[1](root的右子树)。
然后我们就可以发现在l-1和r+1这两个节点中间,夹着一棵树,这棵树包含了我们要的区间[L,R]。
最后再在这棵树上操作就可以了。
这里我们也是用这种方法。
只是R是开始的tree[2]而已,方法还是这样:
计算出所谓的r=l-1(注意:这里的l不是题干中的l,在主函数中我们把l处理成了L的树中的编号)
把tree[2],splay上去。
再处理中间夹着的树就可以了。
*/
int main()
{
scanf("%d%d",&n,&d);
Init();
char op[20];
long long x;
while(n--)
{
scanf("%s",op);
if(op[0]=='A')
{
scanf("%lld",&x);
x=(x+q)%d;
x+=d;
x%=d;
Insert(x);
}
else
{
scanf("%d",&x);
x=tree[root].sz-x;///x处理成L的树中的编号。
printf("%lld\n",q=Get_Max(x));
}
}
return 0;
}