P3960 [NOIP2017 提高组] 列队

题解(参考董晓的写法)


题目链接

P3960 [NOIP2017 提高组] 列队

解题思路

树状数组 + 二分(也可动态开点线段树等)
离线,对每一行,以及最后一列开一个数组存后加值(通过看齐操作而到我这行/列的值),再分别使用树状数组+二分求出每次查询的横(纵)坐标的实际值,然后查询求解。

时间复杂度与空间复杂度分析

树状数组大小 mx=n+q,二分范围 0mxmx次查询,最终复杂度约为 O((n+q)log(n+q))

完整代码

#include <bits/stdc++.h>
#define int long long
#define lowbit(x) (x & -x)
using namespace std;
struct BIT {
int n;
vector<int> s;
BIT(int n) : n(n) {
s.assign(n + 1, {});
}
void update(int x, int k) {
for (int i = x; i <= n; i += lowbit(i)) {
s[i] += k;
}
}
int query(int x) {
int res = 0;
for (int i = x; i; i -= lowbit(i)) {
res += s[i];
}
return res;
}
//以上均为树状数组模板
int binarySearch(int x) { //结合树状数组二分查找给定列(行)x的真实列(行)值
int l = 0, r = n; //原理:对于理应删除的一个节点,不实际删除,而是将其位置元素值变为0,再结合树状数组存储前缀和的性质可以查询真实值
while (l < r) {
int mid = l + r >> 1;
if (query(mid) >= x) r = mid;
else l = mid + 1;
}
return l;
}
};
struct Q {
int y, id;
};
signed main() {
ios::sync_with_stdio(0), cin.tie(0);
int n, m, T;
cin >> n >> m >> T;
int mx = max(n, m) + T; //最大的可能的树状数组大小
vector<int> x(T + 1), y(T + 1);
vector<vector<Q>> v(n + 1);
for (int i = 1; i <= T; ++i) {
cin >> x[i] >> y[i];
if (y[i] != m) v[x[i]].push_back({y[i], i}); //非最后一列的查询元素放入每行的树状数组中
}
BIT bit(mx);
vector<int> col(T + 1); //(一个树状数组循环利用,当作n行的树状数组)
for (int i = 1; i <= mx; ++i) bit.update(i, 1);
for (int i = 1; i <= n; ++i) {
for (auto [y, id] : v[i]) {
col[id] = bit.binarySearch(y); //查询第id个查询真实的列下标
bit.update(col[id], -1); //当前行修改过的元素的 列位置 设置为空
}
for (auto [y, id] : v[i]) {
bit.update(col[id], 1); //还原树状数组,继续用于下一行
}
}
vector<vector<int>> q(n + 1);
for (int i = 1; i <= T; ++i) {
int row = bit.binarySearch(x[i]); //row: 当前行的末尾(第m列)的实际值所在行
int tail = 0, ans = 0; //tail: 当前行末尾实际值
bit.update(row, -1); //同 列的修改
//分类取出真实tail值
if (row <= n) tail = row * m; //tail取自原始的真实行尾值,可直接根据数组下标计算
else tail = q[0][row - n - 1]; //tail取自后面插入的行尾值
//分类取出真实答案ans
if (y[i] == m) ans = tail; //修改的正好是行尾值
else { //修改的不是行尾值
q[x[i]].push_back(tail); //不是行尾值的话,因为行尾值会进入当前行,不再是行尾值,所以需要将行尾值插入当前行的后插入数组中
if (col[i] < m) ans = (x[i] - 1) * m + col[i]; //ans取自原始的真实行内值
else ans = q[x[i]][col[i] - m]; //ans取自当前行的后插入数组中
}
q[0].push_back(ans); //ans需要插入最后一列的后插入数组中
cout << ans << "\n";
}
return 0;
}

AC提交记录

(https://www.luogu.com.cn/record/176544896)
image

posted @   medicos  阅读(4)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通
点击右上角即可分享
微信分享提示