luogu3960 列队
首先我们观察一次操作:对于一次\((x,y)\)离队的操作,它最大的影响范围显然是第\(x\)行和第\(m\)列.
所以我们可以对于每一次操作中纵坐标不是\(m\)的一行一行地处理,最后再来看看最后一列的问题.
首先我们对于每一行建一颗树状数组,维护当前\((x,y)\)离队时取出的数是什么.
attention:这里的数是对于这一行而言的,即编号为\(1,2…m\).
举个例子:
-
现在有一些数:\(1,2,3,4,5,6\)
首先第一个出列:\(2,3,4,5,6,\)7(在最后补齐),\(ans=1\)
第三个出列:\(2,3,5,6,7,8:ans=4\)
……
所以可以得出:
我们只需要在这个数离队时将其赋为\(0\)(减\(1\)),每次查找的时候找到前缀和等于当前要第\(k\)个出列的\(k\)即可.
树状数组的长度记得开\(\max(n,m)+Q\),并且用\(s[i]\)存下第\(i\)个询问在当前行的答案.
然后我们再来处理最后一列以及统计最终的答案.
对于最后一列建一颗树状数组,维护当前\((x,y)\)离队时取出的数是什么.
attention:这里的数是对于最后一列而言的,即编号为\(1,2…m\).
举个例子:
-
现在有一些数:\(1,2,3,4,5,6\)
首先坐标\((1,2)\)出列(横坐标为\(1\)):\(2,3,4,5,6,7:ans=1\)
坐标\((3,4)\)出列(横坐标为\(3\)):\(2,3,5,6,7,8:ans=4\)
……
其实和上面差不多,我就不在赘述,现在看如何统计答案.
对于一次操作\((x,y)\)离队,先看它在最后一列中取出的数,记为\(num\).
如果\(num\leq n\),即他还没有取到最后加入的那些,编号\(ans=num*m\).
反之,不难知道\(ans\)应该是第\(num-n\)次操作时加入的最终答案,即\(Ans[num-n]\).
如果\(y=m\),显然它的离队对于它所在行没有影响,直接储存答案并输出即可.
反之,我们再开一个\(vector\)记一下第\(x\)行新加入的数的编号,即之前算出的\(ans\).
之前我们已经算出第\(i\)个询问在当前行的答案\(s[i]\).
如果\(s[i]<m\),我们可以直接计算答案,\(ans=m*(x[i]-1)+s[i]\),
反之,答案就在第\(x[i]\)行的\(vector\)里面,即\(ans=v[x[i]][s[i]-m]\).
最后储存答案并输出.
一个小优化
朴素的前缀和找法是二分一个\(mid\),比较\(Query(mid)\)与\(k\)的大小,复杂度\(\Theta(log_2^2n)\)
其实可以优化至\(\Theta(log_2n)\):利用二进制的思想.
我们可以脑补一个树状数组的框架:
每一个红色节点储存它的儿子的和.令红色节点为\(c[i]\),黑色节点为\(a[i]\),
于是我们可以运用二进制的思想.
我们从高向低枚举二进制数的位数是否可以为\(1\),如果当前查找的数大于\(tree[statu|(1<<bin)]\),即\(x>\)左边一半的和,我们就将\(statu|=(1<<bin)\),并且将\(x\)减去\(tree[statu|(1<<bin)]\),最后返回\(statu+1\).
为什么要加\(1\)并且是大于而不是大于等于呢?
原因很简单:我们要求\(a[end]\)必须是\(1\),如果我们使用大于等于的话,就可能会有最后一位是零的情况,这是不行的.
cpp
#include<bits/stdc++.h>
#define il inline
#define rg register
#define int ll
#define gi read<int>
using namespace std;
typedef long long ll;
const int O = 3e5 + 10;
template<class TT>
il TT read() {
TT o = 0,fl = 1; char ch = getchar();
while (!isdigit(ch) && ch != '-') ch = getchar();
if (ch == '-') fl = -1, ch = getchar();
while (isdigit(ch)) o = o * 10 + ch - '0', ch = getchar();
return fl * o;
}
struct Data{
int x, y, id;
il bool operator < (Data rhs) const {
return (x ^ rhs.x) ? x < rhs.x : id < rhs.id;
}
}a[O];
int n, m, q, mx, tr[O << 1], x[O], y[O], sol[O];
il void Modify(int x, int delta) {for (; x <= mx; x += (x & (-x))) tr[x] += delta;}
il int Kth(int x) {
int statu = 0;
for (int bin = 1 << 21; bin; bin >>= 1)
if ((statu | bin) <= mx && x > tr[statu | bin])
x -= tr[statu |= bin];
return statu + 1;
}
vector<ll>G[O];
ll Ans[O << 1];
#undef int
int main() {
#define int ll
n = gi(), m = gi(), q = gi(); mx = max(n, m) + q;
for (int i = 1; i <= q; ++i) {
x[i] = gi(), y[i] = gi();
a[i] = (Data){x[i], y[i], i};
}
sort(a + 1, a + ++q);
int lst = 1;
for (int i = 1; i <= mx; ++i) Modify(i, 1);
for (int j = 2; j <= q; ++j)
if (a[j].x ^ a[j - 1].x) {
for (int i = lst; i < j; ++i)
if (a[i].y ^ m)
Modify(sol[a[i].id] = Kth(a[i].y), -1);
for (int i = j - 1; i >= lst; --i)
if (a[i].y ^ m)
Modify(sol[a[i].id], 1);
lst = j;
}
for (int i = 1; i < q; ++i) {
int k = Kth(x[i]); Modify(k, -1);
// printf("%d\n", k);
if (k <= n) Ans[i] = 1ll * k * m;
else Ans[i] = Ans[k - n];
if (y[i] == m) {
printf("%lld\n", Ans[i]);
continue;
}
G[x[i]].push_back(Ans[i]);
printf("%lld\n", Ans[i] = ((sol[i] < m) ? (1ll * (x[i] - 1) * m + sol[i]) : G[x[i]][sol[i] - m]));
}
return 0;
}