解题思路
先模拟一下出队程序。上图打❌的地方当作出队人员,右边的人填过去,之后最后一列下面的人填上去。
如果用数组结构,一个个移动这样模拟的话,自然会TLE掉。
这里要借助值域线段树,用k-th最大的思想解决这个问题。 k-th最大的持久化线段树做法点这里。
我们对每一行的人员进行建线段树。假设有4个人员,一开始是 0 0 0 0,如果要找第2个数,那么就通过一下方程式递归查找。 左边是 m+1-l, 右边是 r-m个数。
int query(int root, int l, int r, int k) {
if (l == r) {
return l; // return the real position
}
int m = (l+r) >> 1;
int num = m-l+1-sum[lson[root]]; // adjust element's position
if (k > num) return query(rson[root], m+1, r, k-num);
else return query(lson[root], l, m, k);
}
如果有一名人员出队,我们在树的那个位置进行+1,之后查找的时候减掉这个多余的(区间大小 - 区间出队次数),这样查询k-th最大的时候就会后移一位了。记录新增的人员编号;如果查询结果大于原队列,在新增人员编号中索引即可
所以我们可以用n+1个线段树对出队问题进行维护。 $O(N*(N«2)$显然会MLE
考虑到问题只有$Q$个操作,每次操作修改$O(logn)$的节点,动态分配就好了。
时间复杂度分析
$O(Q*logN)$
代码
/*
https://www.acwing.com/problem/content/532/
多个线段树进行维护,值域线段树,动态分配节点
Reference:
https://blog.csdn.net/qq_38678604/article/details/78575672
https://www.lagou.com/lgeduarticle/5024.html
*/
#include <iostream>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <cstdio>
#include <vector>
#include <queue>
#include <set>
#include <map>
#include <unordered_map>
#include <stack>
#include <deque>
#define ll long long
#define ull unsigned long long
using namespace std;
const int MOD = 1e9+7;
const int INF = 0x3f3f3f3f;
const int PI = acos(-1.0);
const int N = 3e5+10;
int n, m, q, mx, total = 0;
int sum[N<<5], lson[N<<5], rson[N<<5], rt[N];
vector<ll> G[N]; // N max time of query
int query(int root, int l, int r, int k) {
if (l == r) {
return l; // return the real position
}
int m = (l+r) >> 1;
int num = m-l+1-sum[lson[root]]; // adjust element's position
if (k > num) return query(rson[root], m+1, r, k-num);
else return query(lson[root], l, m, k);
}
void add(int& root, int l, int r, int pos) {
if (root == 0) root = ++total; // dynamically assigning new space!
sum[root]++; // add from top to bottom of this tree if pos is in the interval
if (l < r) {
int m = (l+r) >> 1;
if (pos <= m) add(lson[root], l, m, pos);
else add(rson[root], m+1, r, pos);
}
}
ll verticalSeg(int root, int l, int r, int k, ll v) { // segment tree formed bythe last column
int pos = query(rt[root], l, r, k); add(rt[root], 1, mx, pos);
// cout << "pos" << pos << endl;
ll ans = pos > n ? G[root][pos-n-1]: 1ll * m * pos;
G[root].emplace_back(v ? v:ans); // v==0 or v!=0
return ans;
}
ll horizonalSeg(int root, int l, int r, int k) { // n number of segment tree from n rows all without the rightmost element
int pos = query(rt[root], l, r, k); add(rt[root], 1, mx, pos);
ll ans = pos >= m ? G[root][pos-m]: 1ll*(root-1)*m + pos;
G[root].emplace_back(verticalSeg(n+1, 1, mx, root, ans));
return ans;
}
int main() {
scanf("%d %d %d", &n, &m, &q);
mx = max(n, m) + q; // maximal size of segment tree
memset(sum, 0, sizeof(sum));
memset(rt, 0, sizeof(rt));
memset(lson, 0, sizeof(lson));
memset(rson, 0, sizeof(rson));
for (int i = 0; i < q; ++i) {
int x, y;
scanf("%d %d", &x, &y);
if (y == m) {
printf("%lld\n", verticalSeg(n+1, 1, mx, x, 0));
} else {
printf("%lld\n", horizonalSeg(x, 1, mx, y));
}
}
return 0;
}