1. 程式人生 > 其它 >[Ynoi2019 模擬賽] Yuno loves sqrt technology III

[Ynoi2019 模擬賽] Yuno loves sqrt technology III

題面

Yuno loves sqrt technology III

題解

一道很水的黑題。
做過蒲公英的同學應該都知道這和蒲公英很類似。
不同的是它維護的是區間眾數的個數。
因為區間眾數不具有可加性,再加上強制線上,顯然是用分塊來維護。
首先正常分塊。
預處理塊內資訊時的整合就直接 \(\sqrt(n) ^ 3\) 暴力列舉,得到塊 \(l\) 到 塊 \(r\) 的區間眾數個數。
對於詢問就和正常分塊相同,暴力處理兩邊零散的情況。
但不同於蒲公英的是,這道題資料範圍更大些,再加一個 \(logn\) 的二分查詢,顯然是不行的。
所以我們需要找到一個更快的處理方法。
記錄 \(a[i]\) 在數值 \(a[i]\)

中的次序 \(it\), 以及每個 \(a[i]\) 出現的位置。
首先用 \(ans\) 記錄整塊區間的資訊。
對於左邊零散的部分:
如果 \(it + ans - 1 < a[i]的總個數\),那麼顯然 \(a[i]\) 是有可能更新答案的,那麼我們再看 \(it + ans - 1\) 這個次序的 \(a[i]\) 出現的位置是否在我們詢問的邊界 \(r\) 內,如果是,就說明 \(a[i]\)\(r\) 內是至少有 \(ans + 1\) 個答案的。所以我們就直接更新答案就是了。
對於右邊零散的部分同理,只需要判斷位置是否在左邊界內就行了,具體細節請看碼。

程式碼

#include<cstdio>
#include<cctype>
#include<algorithm>
#include<cmath>
#include<cstring>
#include<vector>

using namespace std;

const int N = 5e5 + 10, M = 710;

int n, m, a[N], b[N], len = 0, tot[N], block, L[M], R[M], Max[M][M], ans = 0, bel[N], cnt[N];

vector < int > pos[N];

inline int query(int l, int r) {
	int p = bel[l], q = bel[r], ans = 0;
	if(p == q) {
		for(int i = l; i <= r; i++) tot[a[i]] = 0;
		for(int i = l; i <= r; i++)
			ans = max(ans, ++tot[a[i]]);
	}
	else {
		ans = Max[p + 1][q - 1];
		for(int i = l; i <= R[p]; i++) {
			int it = cnt[i]; while(it + ans < pos[a[i]].size() && pos[a[i]][it + ans] <= r) ++ans;
		}
		for(int i = L[q]; i <= r; i++) {
			int it = cnt[i]; while(it - ans >= 0 && pos[a[i]][it - ans] >= l) ++ans;
		}
	}
	return ans;
}

inline void read(int &x) {
	x = 0; int f = 1, c = getchar();
	for(; !isdigit(c); c = getchar())
		if(c == '-')
			f = -1;
	for(; isdigit(c); c = getchar())
		x = x * 10 + c - 48;
	x *= f;
}

int main() {
	read(n), read(m);
	for(int i = 1; i <= n; i++) read(a[i]), b[i] = a[i];
	sort(b + 1, b + 1 + n);
	len = unique(b + 1, b + 1 + n) - b - 1;
	for(int i = 1; i <= n; i++)
		a[i] = lower_bound(b + 1, b + 1 + n, a[i]) - b,
		pos[a[i]].push_back(i), cnt[i] = pos[a[i]].size() - 1;
	block = sqrt(n);
	for(int i = 1; i <= block; i++)
		L[i] = (i - 1) * block + 1,
		R[i] = i * block;
	if(R[block] < n) block++, L[block] = R[block - 1] + 1, R[block] = n;
	for(int i = 1; i <= block; i++)
		for(int j = L[i]; j <= R[i]; j++)
			bel[j] = i;
	for(int l = 1; l <= block; l++) {
		memset(tot, 0, sizeof tot);
		for(int r = l; r <= block; r++) {
			Max[l][r] = Max[l][r - 1];
			for(int j = L[r]; j <= R[r]; j++)
				Max[l][r] = max(Max[l][r], ++tot[a[j]]);
		}
	}
	for(int i = 1, l, r; i <= m; i++) {
		read(l), read(r);
		l ^= ans, r ^= ans;
		if(l > r) swap(l, r);
		printf("%d\n", ans = query(l, r));
	}
	return 0;
}