#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
const int N = 2e5 + 10;
struct Node{
int l, r;
int v;
}tr[N * 4];
int n, m, p, last;
void build(int x , int l , int r)
{
tr[x] = {l ,r} ;
if(l == r) return ;
int mid = l + r >> 1 ;
build(x<<1 , l , mid ) , build(x <<1|1 , mid +1 , r) ;
}
void pushup(int x)
{
tr[x].v = max( tr[x<<1].v , tr[x<<1|1].v ) ;
}
// 查询操作 !!
int query(int x , int l , int r)
{
// le = tr[x].l , ri = tr[x].r ; // le 是当前以x为根所代表的 区间 !!!!!!!!!
// 当前以x 为根的区间 被 我们要求的区间( l~r )完全覆盖 !! ,就可以直接返回 tr[x].v(这个区间的最大值)
// l -- le -- ri -- r
if(l <= tr[x].l && tr[x].r <= r) return tr[x].v ;
int mid = tr[x].l + tr[x].r >> 1 ; // mid 取值是从 以x为根的区间内取得中值 !!!!!!!
int v = 0 ;
/*
// 只要我们的 要求的区间(l ~ r)还没有完全覆盖当前 以x为根的子区间
// 就需要一直往下 缩小区间 !!!!
*/
// le -- l -- mid -- ---------- r ;
// 这种情况是 我们要求的 左边界(l) 还在 ( tr[x].l ~ tr[x].r ) 范围内
// 就要往左子树 搜(缩小区间) ;
if(l <= mid )
v = query(x <<1 , l , r) ;
// mid -- r -- ri
// 这种情况是 我们要求的 右边界(r) 还在 ( tr[x].l ~ tr[x].r ) 范围内
// 就要往右子树 搜(缩小区间) ;
if(r > mid)
v = max(v , query(x <<1 |1 , l , r ) ) ; // 对返回的 v 求一个最大值 !!
return v ;
}
// 单点修改操作 !
void modify(int x , int id , int k)
{
if(tr[x].l == tr[x].r && tr[x].l == id ) {
tr[x].v = k ;
}
else {
int mid = tr[x].l + tr[x].r >> 1 ;
if(id <= mid)
modify(x <<1 , id , k) ;
else modify(x <<1 |1 , id , k) ;
pushup(x) ;
// tr[x].v = max(tr[x<<1].v , tr[x<<1|1].v) ; // 跟pushup一样的操作
}
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(0), cout.tie(0);
cin >> m >> p;
build(1, 1, m);
while(m--)
{
string op;
int x;
cin >> op >> x;
if(op == "Q") {
last = query(1, n - x + 1, n);
cout << last << endl;
} else {
modify(1, n + 1, (last + x) % p);
n++;
}
}
return 0;
}