题意
http://codeforces.com/problemset/problem/38/G
给定
n
n
n个数,每个数有两个字段
a
i
,
c
i
a_i, c_i
ai,ci,
a
i
a_i
ai比前面大的就可以往前移,每移一次
c
i
c_i
ci的值就减1(
c
i
=
0
c_i=0
ci=0时不能移动),输出操作之后的序列(原始id)
输入样例:
5
2 3
1 4
4 3
3 1
5 2
输出样例:
3 1 5 4 2
算法一:分块
涉及数列操作一般就是数据结构题:线段树、树状数组、Treap、Splay还有分块,这题也可以用分块完成。
维护
n
\sqrt{n}
n个数列为块,每个块长度
n
\sqrt{n}
n,每块中
a
i
a_i
ai的值总体递减(若不是递减的,那么它的
c
i
=
0
c_i=0
ci=0),并用mx[i]维护每个块i中的最大值。
对于输入的每组数,从最后一个分块开始找合适的块,再在块中插入元素
块长度可能到
n
n
n,因此整体复杂度是
n
n
n \sqrt{n}
nn
分块操作主要是模拟,需要注意一些操作细节。
#include<cstdio>
#include<cstring>
#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;
const int N = 1e5 + 10, SQ = sqrt(1e5+1e3);
// arr[]操作后的有序序列
// idx[]hash数组,存放原始序号
// mx[]每块中的最大值
// LAST块最大编号
int arr[N], idx[N], mx[SQ + 10], LAST = 0, n;
// 分块数组vec[i]为一块,共SQ块
vector <int> vec[SQ + 10];
void remake(int id, int a, int c)
{
vector <int> v;
mx[id] = max(mx[id], a);
while (c > 0 && vec[id].size() > 0 && a > vec[id].back())
c--, v.push_back(vec[id].back()), vec[id].pop_back();
vec[id].push_back(a);
reverse(v.begin(), v.end());
for (int i : v) vec[id].push_back(i);
}
// 将a,c插入到合适的块中
void add(int a, int c)
{
int lst = LAST;
// 找一个合适的块
while (lst > 0 && a > mx[lst] && c >= vec[lst].size())
c -= vec[lst].size(), lst--;
// 在lst这个合适的块中找合适位置(相当于重建这个块)
remake(lst, a, c);
}
void init()
{
int lst = 0;
for (auto v : vec)
for (int i : v)
arr[lst++] = i;
memset(mx, 0, sizeof mx);
for (int i = 0; i < SQ + 10; i ++) vec[i].clear();
//重新分块
for (int i = 0; i < lst; i++)
vec[i/SQ].push_back(arr[i]), mx[i/SQ] = max(mx[i/SQ], arr[i]);
LAST = (lst - 1)/SQ;
}
int main()
{
scanf("%d", &n);
for (int i = 1; i <= n; i ++)
{
int a, c;
scanf("%d%d", &a, &c);
idx[a] = i;
// 将当前值加入当前块
add(a, c);
if (i % SQ == 0)
init();
}
for (auto v : vec)
for (int i : v)
printf("%d ", idx[i]);
return 0;
}
算法二:Treap/Splay
这是Splay的模板题,涉及操作主要是查找和合并。Splay的每个节点保存 a i a_i ai的值、以当前节点为根的子树内的 m a x { a i } max\{a_i\} max{ai}的值。
对于输入的每一组数 a i , c i a_i, c_i ai,ci,先寻找一个合适的插入位置 p p p,使得 a p < a i < a p + 1 a_{p} < a_i < a_{p+1} ap<ai<ap+1(每个 a i a_i ai的值都不一样。再取 p o s = m i n { p , c i , 序 列 长 度 } pos = min\{ p, c_i, 序列长度 \} pos=min{p,ci,序列长度},那么 p o s pos pos就可以旋转到根, p o s + 1 pos+1 pos+1转到 p o s pos pos的右子树,新节点插入到 p o s pos pos的左子树。
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
using namespace std;
const int MAXN = 1e5 + 5;
struct SplayNode
{
int idx;
int v; // 节点值
int s; // 子树大小
int maxv; // 子树最大值
SplayNode * c[2]; // 左右孩子指针
SplayNode * f; // 父节点指针
SplayNode() : s(0) {}
void pushup()
{
s = c[0] -> s + c[1] -> s + 1;
maxv = max(v, max(c[0] -> maxv, c[1] -> maxv));
}
} Tnull, *null = &Tnull;
struct Splay
{
SplayNode node[MAXN];
SplayNode * cur; // 指示node[]的位置
SplayNode * root; // Splay树根
SplayNode * newnode(int v, SplayNode * f, int idx = 0)
{
cur -> idx = idx;
cur -> v = v;
cur -> maxv = v;
cur -> s = 1;
cur -> f = f;
cur -> c[0] = cur -> c[1] = null;
return cur++;
}
void clear()
{
cur = node;
root = newnode(0, null);
root -> c[1] = newnode(0, root);
root -> pushup();
}
// 旋转o和o的父节点,d=0左旋,1右旋
void rotate(SplayNode * o, int d)
{
SplayNode * p = o -> f;
p -> c[!d] = o -> c[d];
o -> c[d] -> f = p;
o -> f = p -> f;
if (p -> f != null)
{
if (p == p -> f -> c[0]) p -> f -> c[0] = o;
else p -> f -> c[1] = o;
}
o -> c[d] = p;
p -> f = o;
p -> pushup();
if (p == root) root = o;
}
// 将o伸展到f下面
void splay(SplayNode * o, SplayNode * f)
{
while (o -> f != f)
{
SplayNode * p = o -> f;
if (p -> f == f)
{
if (o == p -> c[0]) rotate(o, 1);
else rotate(o, 0);
} else {
if (p == p -> f -> c[0])
{
if (o == p -> c[0]) rotate(p, 1), rotate(o, 1);
else rotate(o, 0), rotate(o, 1);
} else {
if (o == p -> c[1]) rotate(p, 0), rotate(o, 0);
else rotate(o, 1), rotate(o, 0);
}
}
}
o -> pushup();
}
// 查找第k个节点,然后伸展到f的下面
void select(int k, SplayNode * f)
{
SplayNode * o = root;
++k;
while(1)
{
int s = o -> c[0] -> s;
if (s + 1 == k)
break;
if (k <= s)
o = o -> c[0];
else {
k -= s + 1;
o = o -> c[1];
}
}
splay(o, f);
}
SplayNode * get(int l, int r)
{
select(l - 1, null);
select(r + 1, root);
return root -> c[1] -> c[0];
}
// 在pos后面插入新节点,其值是v
void insert(int pos, int v, int idx)
{
get(pos + 1, pos);
SplayNode * o = newnode(v, root -> c[1], idx);
root -> c[1] -> c[0] = o;
root -> c[1] -> pushup();
root -> pushup();
}
int get_number(int key)
{
SplayNode * o = root;
int s = 0;
while(o != null)
{
if (o -> c[0] -> maxv > key)
o = o -> c[0];
else if (o -> v > key)
{
s += o -> c[0] -> s;
break;
} else {
s += o -> c[0] -> s + 1;
o = o -> c[1];
}
}
return s - 1;
}
void show(SplayNode * o)
{
if (o -> c[1])
show(o -> c[1]);
if (o -> idx)
printf("%d ", o -> idx);
if (o -> c[0])
show(o -> c[0]);
}
} T;
int n;
void solve()
{
int ai, ci;
int cnt = 0;
T.clear();
for (int i = 1; i <= n; i++)
{
scanf("%d%d", &ai, &ci);
int pos = min(min(T.get_number(ai), ci), cnt);
T.insert(pos, ai, i);
++cnt;
}
T.show(T.root);
}
int main()
{
while(~scanf("%d", &n))
solve();
return 0;
}