0
点赞
收藏
分享

微信扫一扫

ZR #1196. 【线上训练 18】排队


题目链接:​​传送门​​(没买的看不了)

维护队伍中的人的一个班级序列,就是班级相同且相邻的人在一个块内
这样分块的话,一个人插进来的时候只能是在队尾新开一个块或者是插到前面一个块内
所以这个块的序列只会在末尾添加新的元素而不会在中间加入元素
所以用线段树维护每个班级的人数
有人进来时,在线段树上二分查询这个人能进来的最靠前的班级块(先不论是哪个班级)
然后再在vector上二分找最靠前的这个人的班级
接下来只可能是有三种情况:
找不到对应符合条件的班级,自己新开一个
找到了班级,插到里面一个位置中
插到一个班级队伍的最前面,这个要特判
分别判断即可,复杂度

#include <bits/stdc++.h>
#define

using namespace std;
int n, w[A << 2], p, col[A], con;
struct node {int x; vector<int> v;};
vector<node> v[A]; //对每个颜色维护一些块,每个块内都有一些元素
int ask(int k, int l, int r, int &p) {
if (l == r) return l;
int m = (l + r) >> 1;
if (p >= w[k << 1 | 1]) {p -= w[k << 1 | 1]; return ask(k << 1, l, m, p);}
else return ask(k << 1 | 1, m + 1, r, p);
}
void update(int k, int l, int r, int pos) {
if (l == r) {w[k]++; return;}
int m = (l + r) >> 1;
if (pos <= m) update(k << 1, l, m, pos);
else update(k << 1 | 1, m + 1, r, pos);
w[k] = w[k << 1] + w[k << 1 | 1];
}

int main(int argc, char const *argv[]) {
cin >> n;
for (int i = 1, c, a; i <= n; i++) {
scanf("%d%d", &c, &a); //c:第i个人的班级和逼数
p = ask(1, 0, n, a);
int l = 0, r = v[c].size() - 1, ans = -1;
while (l <= r) { //查找第一个位置合法的块
int m = (l + r) >> 1;
if (v[c][m].x >= p) ans = m, r = m - 1;
else l = m + 1;
}
if (ans == -1) { //新开一个颜色块
node nd; nd.x = ++con; nd.v.push_back(i); col[con] = c;
v[c].push_back(nd); update(1, 0, n, con);
}
else if (v[c][ans].x == p)
v[c][ans].v.insert(v[c][ans].v.begin() + v[c][ans].v.size() - a, i), update(1, 0, n, v[c][ans].x); //插到一个块中
else v[c][ans].v.insert(v[c][ans].v.begin(), i), update(1, 0, n, v[c][ans].x); //插到一个块头
}
for (int i = 1; i <= con; i++) {
for (auto j : v[col[i]][0].v) printf("%d ", j);
v[col[i]].erase(v[col[i]].begin());
}
return 0;
}


举报

相关推荐

0 条评论