其他分享
首页 > 其他分享> > 【NOIP2017提高组正式赛】列队

【NOIP2017提高组正式赛】列队

作者:互联网

题解

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

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

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

但他不一定在原队列 \(y\) 位置
因为前面有些点为 \(1\) 空置,一个区间长度减去区间 \(1\) 的个数才是区间有的数
所以我们只要查询排名为 \(y\) 的位置的编号,记为 \(z\)
那么在询问中,当 \(z<m\) 时,它的编号为 \((x-1) \times 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\n", z), S[n + 1].push_back(z);
		else z = move_left(x, y), printf("%lld\n", z), S[n + 1].push_back(z),
			z = move_forward(x), S[x].push_back(z);
	}
}

标签:NOIP2017,正式,int,线段,位置,末端,列队,include,sum
来源: https://www.cnblogs.com/leiyuanze/p/14311399.html