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]\),

\[\therefore tree[i]=a[i-2^k+1]+a[i-2^k+2]+…+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;
}
posted @ 2019-11-06 19:39  wuhan2005  阅读(138)  评论(0编辑  收藏  举报