题意
(摘自洛谷)
【问题描述】 有n个人依次排队,每个人都有两个属性值 a[i] 、c[i] ,a[i]是重要性值,数值越大越重要,c[i]是良心值。假如前i-1人已经排好队后,第i个人来排队,初始时他在队尾,如果他的a[i]大于排在他前面那位的重要性值,那么两人可以交换位置,每次交换良心值减1,直到他前面的人的重要性值大于a[i]或者良心值为0的时候(即最多交换c[i]次),问最终n个人的队列次序。
【输入描述】 第一行一个整数n,表示队列人数。 接下来n行,每行两个整数ai,ci,表示第i个人的重要值和良心值。所有ai是不同的。
【输出描述】 输出队列最终的结果
分析
插队问题,本质上是需要动态维护一个序列.
动态维护序列+交换位置…
很容易联想到splay的top&bottom操作(将序列中某个元素移动至最前面or最后面)
本题使用splay实现.
0.维护tr[u].maxn为u的子树的重要属性最大值.tr[u].id为该点的人的编号(答案)
1.插入元素时,从根结点开始递归,找到符合条件的叶子节点,新建.
2.每次插入之后要把该节点splay到root,(玄学优化).
关于递归插入元素:
1.如果此时待插入的人的事情重要程度小于此时递归到的节点的人(u)的重要程度,则向u的后面插入.
2.如果此时待插入的人的事情重要程度小于此时递归到的节点的右儿子的重要程度最大值(tr[tr[u].s[1]].maxn),则向u的后面插入.
3.如果此时待插入的人的良心值小于等于此时递归到的节点的右儿子的人数(tr[tr[u].s[1]].sz),则向u的后面插入.
4.其他情况,向u的前面插入.
具体代码:
void insert(int &u, int fa, int val, int c, int id)
{
if (u == 0)
{
u = newnode(fa, val);
tr[u].id = id;
return;
}
if (val < tr[u].val || val < tr[tr[u].s[1]].maxn || c <= tr[tr[u].s[1]].sz)
{
insert(tr[u].s[1], u, val, c, id);
}
else
{
insert(tr[u].s[0], u, val, c - tr[tr[u].s[1]].sz - 1, id);
}
pushup(u);
}
这颗splay还算是比较简单的splay,不需要处理哨兵节点,不需要懒标记下放,不需要查询第k大.
代码
#include <bits/stdc++.h>
using namespace std;
//-----pre_def----
const double PI = acos(-1.0);
const int INF = 0x3f3f3f3f;
typedef long long LL;
typedef unsigned long long ULL;
typedef pair<int, int> PII;
typedef pair<double, double> PDD;
#define fir(i, a, b) for (int i = (a); i <= (b); i++)
#define rif(i, a, b) for (int i = (a); i >= (b); i--)
#define endl '\n'
#define init_h memset(h, -1, sizeof h), idx = 0;
#define lowbit(x) x &(-x)
//---------------
const int N = 1e5 + 10;
struct node
{
int s[2], val, p, sz, maxn, id;
} tr[N];
int root, n, m, cnt;
void pushup(int u)
{
tr[u].sz = tr[tr[u].s[0]].sz + tr[tr[u].s[1]].sz + 1;
//tr[u].maxn=max(tr[u].maxn,max(tr[u]))
int a = -1, b = -1;
if (tr[u].s[0])
a = tr[tr[u].s[0]].maxn;
if (tr[u].s[1])
b = tr[tr[u].s[1]].maxn;
tr[u].maxn = max(max(a, b), tr[u].val);
}
void rotate(int x) //旋转
{
int y = tr[x].p, z = tr[y].p;
int k = tr[y].s[1] == x;
tr[z].s[tr[z].s[1] == y] = x;
tr[x].p = z;
tr[y].s[k] = tr[x].s[k ^ 1];
tr[tr[x].s[k ^ 1]].p = y;
tr[x].s[k ^ 1] = y;
tr[y].p = x;
pushup(y), pushup(x);
}
void splay(int x, int k)
{
while (tr[x].p != k)
{
int y = tr[x].p, z = tr[y].p;
if (z != k) //x的祖宗还不是k
{
if ((tr[y].s[1] == x) ^ (tr[z].s[1] == y)) //异或,当zyx不为一条直链的时候
{
rotate(x);
}
else
{
rotate(y);
}
}
rotate(x);
}
pushup(x);
if (!k)
root = x;
}
int newnode(int father, int val)
{
tr[++cnt] = {0, 0, val, father, 1, val};
return cnt;
}
void insert(int &u, int fa, int val, int c, int id)
{
if (u == 0)
{
u = newnode(fa, val);
tr[u].id = id;
return;
}
if (val < tr[u].val || val < tr[tr[u].s[1]].maxn || c <= tr[tr[u].s[1]].sz)
{
insert(tr[u].s[1], u, val, c, id);
}
else
{
insert(tr[u].s[0], u, val, c - tr[tr[u].s[1]].sz - 1, id);
}
pushup(u);
}
void print(int u = root)
{
if (tr[u].s[0])
print(tr[u].s[0]);
printf("%d ", tr[u].id);
if (tr[u].s[1])
print(tr[u].s[1]);
}
void init() {}
int main()
{
#ifndef ONLINE_JUDGE
freopen("in.txt", "r", stdin);
freopen("out.txt", "w", stdout);
int StartTime = clock();
#endif
scanf("%d", &n);
fir(i, 1, n)
{
int a, c;
scanf("%d%d", &a, &c);
insert(root, 0, a, c, i);
splay(cnt, 0);
}
print();
puts("");
#ifndef ONLINE_JUDGE
printf("Run_Time = %d ms\n", clock() - StartTime);
#endif
return 0;
}
把一个u打成了n居然能过67个点…