LG P5048 [Ynoi2019 模拟赛] Yuno loves sqrt technology III Solution

Description

给定序列 a = ( a 1 , a 2 , ⋯   , a n ) a=(a_1,a_2,\cdots,a_n) a=(a1,a2,,an) q q q 次查询 ( l , r ) (l,r) (l,r),求 a l ∼ a r a_l\sim a_r alar 间的众数出现次数.

本题强制在线,每次给定 l ′ , r ′ l^\prime,r^\prime l,r,需要 xor ⁡ \operatorname{xor} xor 上上一次答案得到 l , r l,r l,r,没有则为 0 0 0.

Limitations

1 ≤ n , m , a i ≤ 5 × 10 5 1\le n,m,a_i\le 5\times 10^5 1n,m,ai5×105
1 ≤ l ≤ r ≤ n 1\le l\le r\le n 1lrn
2 s , 62.5 MB 2\text{s},\textcolor{red}{62.5\text{MB}} 2s,62.5MB.

Solution

类似 P4168,先预处理出 f l , r f_{l,r} fl,r 表示第 l ∼ r l\sim r lr 块间的答案.
现在只需考虑散块,由于空间问题,不能套用 P4168 的做法.
设整块内答案为 res \textit{res} res,若散块中有更优答案,其在 a l ∼ a r a_l\sim a_r alar 间出现次数一定大于 res \textit{res} res.

考虑左散块内每个数 a i a_i ai,设 p i p_i pi 表示 a i a_i ai a 1 ∼ a i a_1\sim a_i a1ai 的出现次数,若 a i a_i ai 的第 ( p i + res ) (p_i+\textit{res}) (pi+res) 次出现仍在 [ l , r ] [l,r] [l,r] 内,那么 a i a_i ai 至少 出现了 ( r e s + 1 ) (res+1) (res+1) 次,更新答案,由于是至少,需要重复进行直到不满足以上条件.(右散块同理)

a i a_i ai a a a 中所有出现位置下标显然可用 vector 存(空间复杂度为 O ( n ) O(n) O(n)),于是做完了,注意几点:

  • vector 下标从 0 0 0 开始.
  • 块长开大到 800 800 800 左右.
  • 离散化以节省空间.

由于散块内 res \textit{res} res 最多自增 2 n 2\sqrt n 2n 次,时间复杂度为 O ( n n ) O(n\sqrt n) O(nn ).

Code

3.22 KB , 5.42 s , 11.91 MB    (in   total,   C++20   with   O2) 3.22\text{KB},5.42\text{s},11.91\text{MB}\;\texttt{(in total, C++20 with O2)} 3.22KB,5.42s,11.91MB(in total, C++20 with O2)

#include <bits/stdc++.h>
using namespace std;

using i64 = long long;
using ui64 = unsigned long long;
using i128 = __int128;
using ui128 = unsigned __int128;
using f4 = float;
using f8 = double;
using f16 = long double;

template<class T>
bool chmax(T &a, const T &b){
	if(a < b){ a = b; return true; }
	return false;
}

template<class T>
bool chmin(T &a, const T &b){
	if(a > b){ a = b; return true; }
	return false;
}

namespace fastio {}     // By pystraf
using fastio::read;
using fastio::write;

template<int B>
struct Block {
	int n, v, blocks;
	vector<vector<int>> f;
	vector<basic_string<int>> pos;
	vector<int> cnt, p;
	
	inline Block() {}
	inline Block(int _n, int _v) : n(_n), v(_v) {
		blocks = (n + B - 1) / B;
		f.resize(blocks, vector<int>(blocks, 0));
		cnt.resize(v), pos.resize(v), p.resize(n);
	}
	
	inline int bel(int x) { return x / B; }
	inline int L(int x) { return x * B; }
	inline int R(int x) { return min(L(x) + B, n) - 1; }
	
	inline void clear(int l, int r, const vector<int>& a) {
		for (int i = l; i <= r; i++) cnt[a[i]] = 0;
	}
	
	inline void init(const vector<int>& a) {
		for (int i = 0; i < n; i++) {
	        p[i] = pos[a[i]].size();
	        pos[a[i]] += i;
		}
		
		for (int i = 0; i < blocks; i++) {
		    for (int j = i; j < blocks; j++) {
		    	if (j > i) f[i][j] = f[i][j - 1];
		    	const int bl = L(j), br = R(j);
		    	for (int k = bl; k <= br; k++) chmax(f[i][j], ++cnt[a[k]]);
		    }
		    clear(L(i), n - 1, a);
		}
	}
	
	inline int query(int l, int r, const vector<int>& a) {
		const int bl = bel(l), br = bel(r);
		if (bl == br) {
			int res = 0;
			for (int i = l; i <= r; i++) chmax(res, ++cnt[a[i]]);
			clear(l, r, a);
			return res;
		}
		
		const int lt = R(bl), rt = L(br);
		int res = f[bl + 1][br - 1];
		for (int i = l; i <= lt; i++) 
		    while (res + p[i] < pos[a[i]].size() 
		        && pos[a[i]][res + p[i]] <= r) res++;
		
		for (int i = rt; i <= r; i++) 
		    while (p[i] - res >= 0 && pos[a[i]][p[i] - res] >= l) res++;
		
		return res;
	}
};

constexpr int B = 800;

signed main() {
	ios::sync_with_stdio(0);
	cin.tie(0), cout.tie(0);
	
	const int n = read<int>(), m = read<int>();
	vector<int> a(n), b(n);
	for (int i = 0; i < n; i++) b[i] = a[i] = read<int>();
	
	sort(b.begin(), b.end());
	b.erase(unique(b.begin(), b.end()), b.end());
	for (int i = 0; i < n; i++) {
		a[i] = lower_bound(b.begin(), b.end(), a[i]) - b.begin();
	}
	
	Block<B> blk(n, b.size());
	blk.init(a);
	for (int i = 0, l, r, lst = 0; i < m; i++) {
		l = read<int>(), r = read<int>();
		l ^= lst, r ^= lst, l--, r--;
		if (l > r) swap(l, r);
		write(lst = blk.query(l, r, a));
		putchar_unlocked('\n');
	}
	
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值