P3960 [NOIP2017 提高组] 列队
题解(参考董晓的写法)
题目链接
解题思路
树状数组 + 二分(也可动态开点线段树等)
离线,对每一行,以及最后一列开一个数组存后加值(通过看齐操作而到我这行/列的值),再分别使用树状数组+二分求出每次查询的横(纵)坐标的实际值,然后查询求解。
时间复杂度与空间复杂度分析
树状数组大小
完整代码
#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; }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通