题意:一开始n个人排除一队, 从1~n, 有三种操作:TOP 把第x个人换到最前面;Query输出第x个人的位置;Rank输出第x个位置的是哪个人。
思路:由于N非常大, 很自然想到先离散化,这题的难点主要就在这里了, 离散化处理得好, 下面就好办了 。 这题要把Top 和Query离散化(这两个的x都表示人), 然后记录区间最左边的人是谁, 记录最右边的人是谁, 然后要改下pushup函数不再是+1 而是 +这个区间的人数
对于Top操作, 先把第x个人移动到根节点, 然后再根据他左右孩子的情况把他移动到最前面, 这题有一组很恶心的样例, 最后还要把这个人移动到根结点, 不然会超时
对于Query操作, 直接把第x个人移动到根节点, 左子树结点的总和+1就是他现在的位置
对于Ran操作,直接从根结点往下找
OK附上代码
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 200015;
int root, ch[N][2], fa[N], sz[N];
int num[N], lsh[N], il[N], ir[N], sum[N];
char op[N][6];
inline void pushup(int x)
{
sz[x] = sz[ch[x][0]] + sz[ch[x][1]] + sum[x];
}
void rotate(int x, bool f)
{
int y = fa[x];
int z = fa[y];
ch[y][!f] = ch[x][f];
fa[ch[x][f]] = y;
fa[x] = z;
if(z)
ch[z][ch[z][1] == y] = x;
ch[x][f] = y;
fa[y] = x;
pushup(y);
}
void splay(int x, int g)
{
int y = fa[x];
while(y != g)
{
int z = fa[y];
bool f = (ch[y][0] == x);
if(z != g && f == (ch[z][0] == y))
rotate(y, f);
rotate(x, f);
y = fa[x];
}
pushup(x);
if(g == 0)
root = x;
}
int qcf(int n)
{
int ans = 2;
for(int i = 2; i < n; i++)
if(lsh[i] != lsh[i - 1])
lsh[ans++] = lsh[i];
return ans;
}
void build(int l, int r, int f)
{
if(l > r)
return ;
if(l == r)
{
ch[l][0] = ch[l][1] = 0;
sz[l] = sum[l];
fa[l] = f;
return ;
}
int m = (l + r) >> 1;
fa[m] = f;
ch[m][0] = (m - 1 >= l)? ((l + m - 1) >> 1): 0;
ch[m][1] = (r >= m + 1)? ((m + 1 + r) >> 1): 0;
build(l, m - 1, m);
build(m + 1, r, m);
pushup(m);
}
int bfind(int l, int r, int a)
{
while(l <= r)
{
int m = (l + r) >> 1;
if(il[m] <= a && ir[m] >= a)
return m;
if(ir[m] < a)
l = m + 1;
else
r = m - 1;
}
return -1;
}
void tomin(int x, int r)
{
while(ch[r][0])
{
sz[r] += sz[x];
r = ch[r][0];
}
sz[r] += sz[x];
ch[r][0] = x;
fa[x] = r;
}
void top(int id)
{
splay(id, 0);
int ls = ch[root][0];
if(ls == 0) //这个人已经在最前面了
return ;
if(ch[root][1] == 0) //没有右结点只能把左节点当做根节点
{
root = ls;
fa[root] = 0;
ch[id][0] = ch[id][1] = 0;
sz[id] = sum[id];
tomin(id, ls); //把这个人移动到最前面
return ;
}
root = ch[root][1];
fa[root] = 0;
ch[id][0] = ch[id][1] = 0;
sz[id] = sum[id];
tomin(id, ls); //把这个人移动到最前面
tomin(ls, root); //把左节点移动到根节点
splay(id, 0);//这句话相当重要, 不加就会超时= 0 =, 这题有一组恶心的数据
}
int que(int id)
{
splay(id, 0);
return sz[ch[root][0]] + 1;
}
int ran(int x)
{
x--;
int r = root;
while(x != sz[ch[r][0]])
{
if(x < sz[ch[r][0]])
r = ch[r][0];
else
{
x -= sz[ch[r][0]] + sum[r];
if(x < 0)
return il[r] + sum[r] + x;
r = ch[r][1];
}
}
return il[r];
}
int main()
{
int T, C = 1;
scanf("%d", &T);
while(T--)
{
int n, m;
scanf("%d%d", &n , &m);
int cnt = 1;
for(int i = 0; i < m; i++)
{
scanf("%s%d", op[i], num + i);
if(op[i][0] != 'R')
lsh[cnt++] = num[i];
}
printf("Case %d:\n", C++);
if(cnt > 1)
{
sort(lsh + 1, lsh + cnt);
cnt = qcf(cnt); //去重复
int ans = 1;
if(lsh[1] == 1)
{
il[1] = 1;
ir[1] = 1;
sum[1] = 1;
}
else
{
il[1] = 1;
ir[1] = lsh[1] - 1;
sum[1] = lsh[1] - 1;
il[2] = ir[2] = lsh[1];
sum[2] = 1;
ans = 2;
}
for(int i = 2; i < cnt; i++)
{
if(lsh[i] - lsh[i - 1] > 1)
{
il[++ans] = lsh[i - 1] + 1;
ir[ans] = lsh[i] - 1;
sum[ans] = ir[ans] - il[ans] + 1;
}
il[++ans] = lsh[i];
ir[ans] = lsh[i];
sum[ans] = 1;
}
if(ir[ans] != n)
{
ir[++ans] = n;
il[ans] = ir[ans - 1] + 1;
sum[ans] = ir[ans] - il[ans] + 1;
}
fa[0] = sz[0] = ch[0][0] = ch[0][1] = 0;
root = (1 + ans) >> 1;
build(1, ans, 0);
for(int i = 0; i < m; i++)
{
if(op[i][0] == 'T')
{
int id = bfind(1, ans, num[i]);
top(id);
}
else if(op[i][0] == 'Q')
{
int id = bfind(1, ans, num[i]);
printf("%d\n", que(id));
}
else
{
printf("%d\n", ran(num[i]));
}
}
}
else
{
for(int i = 0; i < m; i++)
printf("%d\n", num[i]);
}
}
return 0;
}