【NOIP2017提高组正式赛】列队

题解

本题的解法是丰富多彩的!
线段树做法是极好的
代码非常之少

一个很显然的想法是维护 (n+1) 颗线段树
那要怎么维护才能不爆空间呢?
我们发现尽管 (n imes m) 那么大
(q) 相比 (n imes m) 小得多
也就是说变动的点比较少
于是我们抓住这点
考虑只记录变动的点
行的线段树维护 ([1,m-1]) 位置的信息
列的线段树维护 ([1,n]) 的位置的信息

那我一颗线段树就记录那些点不在原来位置了
也就是说一颗线段树长为 (max(n,m)+q) (为什么?待会再解释)
每一位只为 (0)(1) 表示 没变或变了
(1) 表示变了是因为没变的太多无法记录,那就记变的吧
这样线段树动态开点空间就可以接受了
然后变动的点我们考虑它后来的位置
依题知它会到最后一列的末端,然后最后一列它所在的行的点会到它所在行的线段是的末端
我们无法向平衡树一样动态移动(插入,删除)点,但题目只需插入到末端
所以我们预留空间把插入的点放到末端,然后它原来位置标记为 (1),表示此处无点
对于查询,我们知道为第 (x(x<=n)) 颗线段树的 (y) 位置的点的编号为 ((x-1) imes m+y)

但他不一定在原队列 (y) 位置
因为前面有些点为 (1) 空置,一个区间长度减去区间 (1) 的个数才是区间有的数
所以我们只要查询排名为 (y) 的位置的编号,记为 (z)
那么在询问中,当 (z<m) 时,它的编号为 ((x-1) imes m+z)
(z >= m) 时说明前面有空位,有点移动到后面了
我们用 (n+1)(vector) 储存每个线段树依次移动到后面的点,输出 (S[x][z-m]) 即可
列同理

(Code)

#include<cstdio>
#include<vector>
#include<iostream>
#define LL long long
using namespace std;

const int N = 3e5 + 5, M = 6e6 + 5;
int n, m, q, sum[M], ls[M], rs[M], rt[N], Len, size;
vector<LL> S[N];

void update(int &p, int l, int r, int x)
{
	if (!p) p = ++size;
	++sum[p];
	if (l == r) return;
	int mid = (l + r) >> 1;
	if (x <= mid) update(ls[p], l, mid, x);
	else update(rs[p], mid + 1, r, x);
}

int query(int p, int l, int r, int k)
{
	if (l == r) return l;
	int mid = (l + r) >> 1, s = mid - l + 1 - sum[ls[p]];
	if (k <= s) return query(ls[p], l, mid, k);
	return query(rs[p], mid + 1, r, k - s);
}

inline LL move_forward(int x)
{
	int p = query(rt[n + 1], 1, Len, x);
	update(rt[n + 1], 1, Len, p);
	return (p <= n ? (1LL * p * m) : (S[n + 1][p - n - 1]));
}

inline LL move_left(int x, int y)
{
	int p = query(rt[x], 1, Len, y);
	update(rt[x], 1, Len, p);
	return (p < m ? (1LL * (x - 1) * m + p) : (S[x][p - m]));
}

int main()
{
	freopen("phalanx.in", "r", stdin);
	freopen("phalanx.out", "w", stdout);
	scanf("%d%d%d", &n, &m, &q);
	Len = max(n, m) + q;
	for(int x, y; q; --q)
	{
		scanf("%d%d", &x, &y);
		LL z;
		if (y == m) z = move_forward(x), printf("%lld
", z), S[n + 1].push_back(z);
		else z = move_left(x, y), printf("%lld
", z), S[n + 1].push_back(z),
			z = move_forward(x), S[x].push_back(z);
	}
}
原文地址:https://www.cnblogs.com/leiyuanze/p/14311399.html