[JOISC 2016 Day 3] 回转寿司 题解

又是一道满级性质题,第一次在分块里面打堆这种东西。

感觉做了一堆 \(\texttt{Ynoi}\) 之后,还是啥也不会。

首先这种神奇题目,还有这个神级时限,没有很好的办法处理的时候,我们考虑对序列分块,设块长为 \(B\)

环不用去管,纯粹就是懒得好好造数据,直接破开就可以了。

需要明确的一点是,如果一个数经过了一整块之后,那么这个数就会变成这个块的最大值。(前提是他变了。)

所以对于一个数在整块上的处理是比较简单的,维护一个大根堆,随时跟进这个块上有哪些数就行。然后操作的时候替换就可以了。

但是散块呢?

首先修改散块是非常简单的,就是暴力修。但是我们现在要思考的是如何去查询散块,因为你并没有在修改整块的时候跟进每个数的值。

我们仔细思考可以发现(???),对于两个数 \(x,y\),你对一段序列进行操作时,无论你是先加 \(x\),还是先加 \(y\),他的结果是一样的,也就是说,最终结果与你加入数字的先后没有关系。

那这个问题就会方便很多,我们可以对于每个块维护一个小根堆,表示整个块当前加入了哪些数。可以发现,我们为了得到一个散块的具体值,在暴力重构的时候,如果我们按照小根堆的顺序进行加入时,我们就可以直接从左到右枚举块上的所有数,如果这个数大于小根堆的根,那就把这个根删掉,然后将这个数加入小根堆后,替换为这个根(正确性由替换条件 \(a_i >x\) 和小根堆每次取出的根的单调性可得),就可以完美在 \(B\times \log m\) 次操作下实现重构,从而做到查询散块。(当然重构结束之后,也理应更改大根堆的值,并清空这个小根堆)

总时间复杂度,整块由于存在替换,为 \(\mathcal{O}(m\ \dfrac{n}{B} \log B)\),散块暴力修改,为 \(\mathcal{O}(mB)\),暴力重构复杂度为 \(\mathcal{O}(mB\log m)\)

时限够大,\(B=\sqrt{n}\) 绰绰有余。

代码

#include <bits/stdc++.h>
using namespace std;
#define maxn 400005
int n, q, a[maxn];
int l[maxn], r[maxn], belong[maxn];
priority_queue<int> p1[705];
priority_queue<int, vector<int>, greater<int> > p2[705];
void init()
{
	int len = sqrt(n);
	for (int i = 1; i <= len; ++i)
	{
		l[i] = (i - 1) * len + 1, r[i] = i * len;
		for (int j = l[i]; j <= r[i]; ++j) belong[j] = i; 
	}
	if(len * len < n)
	{
		l[len + 1] = len * len + 1, r[len + 1] = n;
		++len;
		for (int j = l[len]; j <= n; ++j) belong[j] = len;
	}
	for (int i = 1; i <= len; ++i)
	{
		for (int j = l[i]; j <= r[i]; ++j)
		p1[i].push(a[j]);
	}
}
void rebuild(int p)
{
	if(p2[p].empty()) return;
	for (int i = l[p]; i <= r[p]; ++i)
	{
		if(a[i] > p2[p].top())
		{
			int x = p2[p].top(); p2[p].pop();
			p2[p].push(a[i]), a[i] = x;
		}
	}
	while(!p2[p].empty()) p2[p].pop();
}
int bruce(int p, int l, int r, int x)
{
	while(!p1[p].empty()) p1[p].pop();
	for (int i = l; i <= r; ++i) if(a[i] > x) swap(a[i], x);
	for (int i = ::l[p]; i <= ::r[p]; ++i) p1[p].push(a[i]);
	return x;
}
int query(int l, int r, int x)
{
	int p = belong[l], q = belong[r];
	rebuild(p), rebuild(q);
	if(p == q) return bruce(p, l, r, x);
	else
	{
		x = bruce(p, l, ::r[p], x);
		for (int i = p + 1; i < q; ++i)
		{
			int y = p1[i].top();
			if(y <= x) continue;
			p1[i].pop(), p1[i].push(x), p2[i].push(x);
			x = y;
		}
		return x = bruce(q, ::l[q], r, x);
	}
}
int main()
{
	scanf("%d %d", &n, &q);
	for (int i = 1; i <= n; ++i) scanf("%d", &a[i]);
	init();
	while(q--)
	{
		int l, r, x;
		scanf("%d %d %d", &l, &r, &x);
		int ans;
		if(l <= r) ans = query(l, r, x);
		else ans = query(l, n, x), ans = query(1, r, ans);
		printf("%d\n", ans);
	}
	return 0;
}
posted @ 2024-03-07 16:17  Saltyfish6  阅读(5)  评论(0编辑  收藏  举报
Document