数据结构。
n = 1 n=1 n=1 的 case:考虑有 m + q m+q m+q 个位置,每次操作队移,出队人直接插入队尾。维护位置对应的人,每次查询第 k k k 个人的位置 p p p,输出 p p p 位置对应的人,并将出对者加入队尾。
实现考虑维护 01 序列,表示位置上是 / 否有人,每次查前缀和为 k k k 的位置即可。
一般情况:每次操作只会影响某一行以及最后一列。考虑将最后一列单独处理。
对于查询 ( x , y ) (x,y) (x,y):需查询第 x x x 行第 y y y 个人的位置以及最后一列第 x x x 个人的位置( y = m y = m y=m 时只用查最后一列),同 n = 1 n=1 n=1 的 case 维护一下对应编号即可。
实现考虑离线树状树组或动态开点线段树,线段树 / 树状树组上二分可以做到 log \log log,总时间复杂度 O ( n log n ) O(n \log n) O(nlogn)。
树状树组实现显然要离线,仅用一个 BIT 预处理每次非最后一列操作在对应行的位置。
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N = 3e5 + 5;
const int V = 6e5;
int n, m, q, tree[N<<1], x[N], y[N], num[N];
int lowbit(int x) {return x&(-x);}
void add(int x, int d){for(;x <= V; x += lowbit(x)) tree[x] += d;}
int select(int k)
{
int pos = 0, sum = 0;
for(int i=20; i>=0; i--)
{
pos += (1 << i);
if(pos > V or sum + tree[pos] >= k) pos -= (1 << i);
else sum += tree[pos];
}
return pos + 1;
}
signed main()
{
ios::sync_with_stdio(0); cin.tie(0);
cin >> n >> m >> q;
for(int i=1; i<=V; i++) tree[i] = lowbit(i);
vector <vector<int>> cmd(n+1);
for(int i=1; i<=q; i++)
{
cin >> x[i] >> y[i];
if(y[i] != m) cmd[x[i]].push_back(i);
}
for(int i=1; i<=n; i++)
{
for(auto id : cmd[i])
num[id] = select(y[id]), add(num[id], -1);
for(auto id : cmd[i])
add(num[id], 1);
}
vector <vector<int>> row(n+1);
vector <int> column;
for(int i=1; i<=q; i++)
{
int in, out, p = select(x[i]);
add(p, -1);
if(y[i] != m)
{
out = (num[i] < m) ? ((x[i]-1)*m+num[i]) : row[x[i]][num[i]-m],
in = (p <= n) ? (p*m) : column[p-n-1];
row[x[i]].push_back(in), column.push_back(out);
}
else
{
out = (p <= n) ? (p*m) : column[p-n-1];
column.push_back(out);
}
cout << out << "\n";
}
}