P3165 [CQOI2014]排序机械臂
不难,但是挺坑的。。。
这道题要你解决的问题比较显然能总结出来:每次找出第i大的元素所在位置p,然后对\([i,p]\)区间翻转,每次询问这个第i大的元素所在位置。
看到翻转操作就想到了平衡树,所以用splay解决。
我们可以预处理出原序列中第k大的元素是在第几个。离散化排序一下就是了。
我们从1到n+2这一段数字建一个splay,然后用离散化后数组就可以遍历每一次那个第k大的下标。
为什么要从1到n+2?下面要用到split操作。
如何查找排名?直接把这个东西splay到根,输出左子树size和它自己的。因为引入了虚拟结点所以自己的那个1都不用加。
然后就直接split出这段区间然后搞一个标记即可。
然后发现pushdown这个东西有一个难以实现的地方:我每次print之后答案是对的,但是没有整棵树遍历的话答案是错的。
那么就是你pushdown少了,除了在查找第k大的时候pushdown,在splay的时候也可以pushdown。具体看代码。
注意pushdown的顺序也是从上到下的。
有一个坑点:需要注意的是,如果有高度相同的物品,必须保证排序后它们的相对位置关系与初始时相同。
所以你离散化的时候需要注意,否则爆零。。。
代码:
#include<cstdio>
#include<algorithm>
const int maxn = 100005;
struct Nodes
{
int val, idx;
} a[maxn];
int size[maxn], fa[maxn], ch[maxn][2], val[maxn];
int rev[maxn];
int root;
int n;
bool cmp(const Nodes x, const Nodes y)
{
if(x.val == y.val) return x.idx < y.idx;
return x.val < y.val;
}
int read()
{
int ans = 0, s = 1;
char ch = getchar();
while(ch > '9' || ch < '0'){ if(ch == '-') s = -1; ch = getchar(); }
while(ch >= '0' && ch <= '9') ans = (ans << 1) + (ans << 3) + ch - '0', ch = getchar();
return s * ans;
}
int dir(int x)
{
return ch[fa[x]][1] == x;
}
void connect(int son, int f, int k)
{
fa[son] = f;
ch[f][k] = son;
}
void pushup(int x)
{
size[x] = size[ch[x][0]] + size[ch[x][1]] + 1;
}
void pushdown(int x)
{
if(rev[x])
{
if(ch[x][0]) rev[ch[x][0]] ^= 1;
if(ch[x][1]) rev[ch[x][1]] ^= 1;
std::swap(ch[x][0], ch[x][1]);
rev[x] = 0;
}
}
void rotate(int x)
{
int y = fa[x];
int z = fa[y];
int yk = dir(x);
int zk = dir(y);
int b = ch[x][yk ^ 1];
connect(b, y, yk);
connect(y, x, yk ^ 1);
connect(x, z, zk);
pushup(y);
pushup(x);
}
void splay(int x, int goal)
{
while(fa[x] != goal)
{
int y = fa[x];
int z = fa[y];
pushdown(z);
pushdown(y);
pushdown(x);
if(z != goal) dir(x) == dir(y) ? rotate(y) : rotate(x);
rotate(x);
}
if(goal == 0) root = x;
}
int kth(int k)
{
int now = root;
while(2333)
{
pushdown(now);
if(size[ch[now][0]] + 1 < k)
{
k -= size[ch[now][0]] + 1;
now = ch[now][1];
}
else if(size[ch[now][0]] >= k) now = ch[now][0];
else return now;
}
}
int build(int f, int l, int r)
{
if(l > r) return 0;
if(l == r)
{
fa[l] = f;
size[l] = 1;
val[l] = l;
return l;
}
int mid = (l + r) >> 1;
fa[mid] = f;
val[mid] = mid;
ch[mid][0] = build(mid, l, mid - 1);
ch[mid][1] = build(mid, mid + 1, r);
pushup(mid);
return mid;
}
void print(int u)
{
pushdown(u);
if(ch[u][0]) print(ch[u][0]);
printf("%d ", val[u]);
if(ch[u][1]) print(ch[u][1]);
}
int main()
{
n = read();
for(int i = 2; i <= n + 1; i++) a[i].val = read(), a[i].idx = i;
root = build(0, 1, n + 2);
std::sort(a + 2, a + n + 2, cmp);
for(int i = 2; i <= n + 1; i++)
{
splay(a[i].idx, 0);
int temp = size[ch[root][0]];
printf("%d ", temp);
int pre = kth(i - 1), post = kth(temp + 2);
splay(pre, 0), splay(post, pre);
int del = ch[post][0];
rev[del] ^= 1;
//printf("---- "); print(root); printf("\n");
}
return 0;
}