Loading

「学习笔记」数列分块入门 1 ~ 9

一天多一点的时间, 做完了这 \(9\) 道题, 除了最后一道题之外, 都感觉良好.
这里是 黄学长的博客.

数列分块入门 1

区间加法, 单点查值. 很入门的题目了.
暴力处理两边不完整的块, 完整的块维护一个 tag 加法标记.

/*
  The code was written by yifan, and yifan is neutral!!!
 */

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

template<typename T>
inline T read() {
	T x = 0;
	bool fg = 0;
	char ch = getchar();
	while (ch < '0' || ch > '9') {
		fg |= (ch == '-');
		ch = getchar();
	}
	while (ch >= '0' && ch <= '9') {
		x = (x << 3) + (x << 1) + (ch ^ 48);
		ch = getchar();
	}
	return fg ? ~x + 1 : x;
}

const int N = 5e4 + 5;

int n, len;
int be[N];
ll val[N], tag[N];

void add(int l, int r, int c) {
	int lim = min(r, be[l] * len);
	for (int i = l; i <= lim; ++ i) {
		val[i] += c;
	}
	if (be[l] != be[r]) {
		for (int i = (be[r] - 1) * len + 1; i <= r; ++ i) {
			val[i] += c;
		}
	}
	for (int i = be[l] + 1; i <= be[r] - 1; ++ i) {
		tag[i] += c;
	}
}

int main() {
	n = read<int>(), len = sqrt(n);
	for (int i = 1; i <= n; ++ i) {
		val[i] = read<ll>();
	}
	for (int i = 1; i <= n; ++ i) {
		be[i] = (i - 1) / len + 1;
	}
	while (n --) {
		int op = read<int>(), a = read<int>(), b = read<int>(), c = read<int>();
		if (op == 0) {
			add(a, b, c);
		}
		else {
			printf("%lld\n", val[b] + tag[be[b]]);
		}
	}
	return 0;
}

数列分块入门 2

区间加法, 查询区间内小于某个值的元素个数.
vectorsort 就可以解决, 我一开始用的 set, 后来发现没法获取数组下标.

/*
  The code was written by yifan, and yifan is neutral!!!
 */

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define le(i) ((i - 1) * len + 1)
#define re(i) (i * len)

template<typename T>
inline T read() {
	T x = 0;
	bool fg = 0;
	char ch = getchar();
	while (ch < '0' || ch > '9') {
		fg |= (ch == '-');
		ch = getchar();
	}
	while (ch >= '0' && ch <= '9') {
		x = (x << 3) + (x << 1) + (ch ^ 48);
		ch = getchar();
	}
	return fg ? ~x + 1 : x;
}

const int N = 5e4 + 5;
const int inf = (1 << 31);
const int INF = ~(1 << 31);

int n, len;
int be[N];
int val[N], tag[N], mx[N], mn[N];

inline void add(int l, int r, ll c) {
	int lim = min(r, re(be[l]));
	for (int i = l; i <= lim; ++ i) {
		val[i] += c;
		mx[be[l]] = max(mx[be[l]], val[i]);
		mn[be[l]] = min(mn[be[l]], val[i]);
	}
	if (be[l] == be[r])	return ;
	for (int i = le(be[r]); i <= r; ++ i) {
		val[i] += c;
		mx[be[r]] = max(mx[be[r]], val[i]);
		mn[be[r]] = min(mn[be[r]], val[i]);
	}
	for (int i = be[l] + 1; i <= be[r] - 1; ++ i) {
		tag[i] += c;
	}
}

inline int ask(int l, int r, ll c) {
	int ans = 0, lim = min(r, re(be[l]));
	for (int i = l; i <= lim; ++ i) {
		if (mn[be[l]] + tag[be[l]] >= c) {
			break ;
		}
		if (mx[be[l]] + tag[be[l]] < c) {
			ans += (lim - l + 1);
			break ;
		}
		(val[i] + tag[be[l]] < c) ? ++ ans : ans;
	}
	if (be[l] == be[r])	return ans;
	for (int i = le(be[r]); i <= r; ++ i) {
		if (mn[be[r]] + tag[be[r]] >= c) {
			break ;
		}
		if (mx[be[r]] + tag[be[r]] < c) {
			ans += (r - le(be[r]) + 1);
			break ;
		}
		(val[i] + tag[be[r]] < c) ?	++ ans : ans;
	}
	for (int i = be[l] + 1; i <= be[r] - 1; ++ i) {
		if (mn[i] + tag[i] >= c) {
			continue ;
		}
		if (mx[i] + tag[i] < c) {
			ans += len;
			continue ;
		}
		for (int j = le(i); j <= re(i); ++ j) {
			(val[j] + tag[i] < c) ? ++ ans : ans;
		}
	}
	return ans;
}

int main() {
	n = read<int>(), len = sqrt(n);
	memset(mx, 128, sizeof mx);
	memset(mn, 127, sizeof mn);
	for (int i = 1; i <= n; ++ i) {
		val[i] = read<ll>();
		be[i] = (i - 1) / len + 1;
		mx[be[i]] = max(mx[be[i]], val[i]);
		mn[be[i]] = min(mn[be[i]], val[i]);
	}
	for (int i = 1; i <= n; ++ i) {
		int op = read<int>(), a = read<int>(), b = read<int>(), c = read<int>();
		if (op == 0) {
			add(a, b, c);
		}
		else {
			printf("%d\n", ask(a, b, c * c));
		}
	}
	return 0;
}

数列分块入门 3

区间加法, 找前驱.
这已经是平衡树的操作了, 但是这里我们不写平衡树 我也已经忘了咋写了 =_= ||, 采取在块上二分的方法.
这里有 setvector 两种写法.

vector 写法

/*
  The code was written by yifan, and yifan is neutral!!!
 */

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define le(i) ((i - 1) * len + 1)
#define re(i) (i * len)

template<typename T>
inline T read() {
	T x = 0;
	bool fg = 0;
	char ch = getchar();
	while (ch < '0' || ch > '9') {
		fg |= (ch == '-');
		ch = getchar();
	}
	while (ch >= '0' && ch <= '9') {
		x = (x << 3) + (x << 1) + (ch ^ 48);
		ch = getchar();
	}
	return fg ? ~x + 1 : x;
}

const int N = 1e5 + 5;

int n, len;
int be[N];
int val[N], tag[N];
vector<int> s[510];

inline void reset(int x) {
	s[x].clear();
	for (int i = le(x); i <= re(x); ++ i) {
		s[x].emplace_back(val[i]);
	}
	sort(s[x].begin(), s[x].end());
}

inline void add(int l, int r, ll c) {
	int lim = min(r, re(be[l]));
	for (int i = l; i <= lim; ++ i) {
		val[i] += c;
	}
	reset(be[l]);
	if (be[l] == be[r])	return ;
	for (int i = le(be[r]); i <= r; ++ i) {
		val[i] += c;
	}
	reset(be[r]);
	for (int i = be[l] + 1; i <= be[r] - 1; ++ i) {
		tag[i] += c;
	}
}

inline int ask(int l, int r, ll c) {
	int ans = -1;
	int lim = min(r, re(be[l]));
	for (int i = l; i <= lim; ++ i) {
		int tmp = val[i] + tag[be[l]];
		if (tmp < c) {
			ans = max(ans, tmp);
		}
		if (ans == c - 1) {
			return ans;
		}
	}
	if (be[l] == be[r])	return ans;
	for (int i = le(be[r]); i <= r; ++ i) {
		int tmp = val[i] + tag[be[r]];
		if (tmp < c) {
			ans = max(ans, tmp);
		}
		if (ans == c - 1) {
			return ans;
		}
	}
	for (int i = be[l] + 1; i <= be[r] - 1; ++ i) {
		if (*s[i].begin() + tag[i] >= c)	continue ;
		int pos = lower_bound(s[i].begin(), s[i].end(), c - tag[i]) - s[i].begin();
		ans = max(ans, s[i][pos - 1] + tag[i]);
		if (ans == c - 1) {
			return ans;
		}
	}
	return ans;
}

int main() {
	n = read<int>(), len = sqrt(n);
	for (int i = 1; i <= n; ++ i) {
		val[i] = read<ll>();
		be[i] = (i - 1) / len + 1;
		s[be[i]].emplace_back(val[i]);
	}
	for (int i = 1; i <= len; ++ i) {
		sort(s[i].begin(), s[i].end());
	}
	for (int i = 1; i <= n; ++ i) {
		int op = read<int>(), a = read<int>(), b = read<int>(), c = read<int>();
		if (op == 0) {
			add(a, b, c);
		}
		else {
			printf("%d\n",ask(a, b, c));
		}
	}
	return 0;
}

set 写法

/*
  The code was written by yifan, and yifan is neutral!!!
 */

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define le(i) ((i - 1) * len + 1)
#define re(i) (i * len)

template<typename T>
inline T read() {
	T x = 0;
	bool fg = 0;
	char ch = getchar();
	while (ch < '0' || ch > '9') {
		fg |= (ch == '-');
		ch = getchar();
	}
	while (ch >= '0' && ch <= '9') {
		x = (x << 3) + (x << 1) + (ch ^ 48);
		ch = getchar();
	}
	return fg ? ~x + 1 : x;
}

const int N = 1e5 + 5;

int n, len;
int be[N];
ll val[N], tag[N];
multiset<ll> s[1100];

void add(int l, int r, ll c) {
	int lim = min(r, re(be[l]));
	for (int i = l; i <= lim; ++ i) {
		s[be[l]].erase(val[i]);
		val[i] += c;
		s[be[l]].insert(val[i]);
	}
	if (be[l] == be[r])	return ;
	for (int i = le(be[r]); i <= r; ++ i) {
		s[be[r]].erase(val[i]);
		val[i] += c;
		s[be[r]].insert(val[i]);
	}
	for (int i = be[l] + 1; i <= be[r] - 1; ++ i) {
		tag[i] += c;
	}
}

ll ask(int l, int r, ll c) {
	ll ans = -1;
	int lim = min(r, re(be[l]));
	for (int i = l; i <= lim; ++ i) {
		ll tmp = val[i] + tag[be[l]];
		if (tmp < c) {
			ans = max(ans, tmp);
		}
	}
	if (be[l] == be[r])	return ans;
	for (int i = le(be[r]); i <= r; ++ i) {
		ll tmp = val[i] + tag[be[r]];
		if (tmp < c) {
			ans = max(ans, tmp);
		}
	}
	for (int i = be[l] + 1; i <= be[r] - 1; ++ i) {
		ll tmp = c - tag[i];
		set<ll> :: iterator it = s[i].lower_bound(tmp);
		if (it == s[i].begin()) {
			continue ;
		}
		-- it;
		ans = max(ans, *it + tag[i]);
	}
	return ans;
}

int main() {
	n = read<int>(), len = sqrt(n);
	for (int i = 1; i <= n; ++ i) {
		val[i] = read<ll>();
		be[i] = (i - 1) / len + 1;
		s[be[i]].insert(val[i]);
	}
	for (int i = 1; i <= n; ++ i) {
		int op = read<int>(), a = read<int>(), b = read<int>(), c = read<int>();
		if (op == 0) {
			add(a, b, c);
		}
		else {
			printf("%lld\n", ask(a, b, c));
		}
	}
	return 0;
}

数列分块入门 4

区间加法, 区间求和.
暴力处理两边不完整的块, 完整的块记录总和与加法标记, 类似于线段树的操作.

/*
  The code was written by yifan, and yifan is neutral!!!
 */

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define le(i) ((i - 1) * len + 1)
#define re(i) (i * len)

template<typename T>
inline T read() {
	T x = 0;
	bool fg = 0;
	char ch = getchar();
	while (ch < '0' || ch > '9') {
		fg |= (ch == '-');
		ch = getchar();
	}
	while (ch >= '0' && ch <= '9') {
		x = (x << 3) + (x << 1) + (ch ^ 48);
		ch = getchar();
	}
	return fg ? ~x + 1 : x;
}

const int N = 50010;

int n, len;
int be[N];
ll val[N], tag[N], sum[510];

inline void add(int l, int r, ll c) {
	int lim = min(r, re(be[l]));
	for (int i = l; i <= lim; ++ i) {
		val[i] += c;
		sum[be[l]] += c;
	}
	if (be[l] == be[r])	return ;
	for (int i = le(be[r]); i <= r; ++ i) {
		val[i] += c;
		sum[be[r]] += c;
	}
	for (int i = be[l] + 1; i <= be[r] - 1; ++ i) {
		tag[i] += c;
	}
}

inline ll ask(int l, int r, ll c) {
	ll res = 0;
	int lim = min(r, re(be[l]));
	for (int i = l; i <= lim; ++ i) {
		res = (res + val[i] + tag[be[l]]) % c;
	}
	if (be[l] == be[r])	return res;
	for (int i = le(be[r]); i <= r; ++ i) {
		res = (res + val[i] + tag[be[r]]) % c;
	}
	for (int i = be[l] + 1; i <= be[r] - 1; ++ i) {
		res = (res + sum[i] + tag[i] * len) % c;
	}
	return res;
}

int main() {
	n = read<int>(), len = sqrt(n);
	for (int i = 1; i <= n; ++ i) {
		val[i] = read<int>();
		be[i] = (i - 1) / len + 1;
		sum[be[i]] += val[i];
	}
	for (int i = 1, op, l, r, c; i <= n; ++ i) {
		op = read<int>(), l = read<int>(), r = read<int>(), c = read<int>();
		if (op == 0) {
			add(l, r, c);
		}
		else {
			printf("%lld\n", ask(l, r, c + 1));
		}
	}
	return 0;
}

数列分块入门 5

区间开方, 区间求和.
一个 long long 范围的数, 最多开 \(6\) 次平方就会变成 \(1\)\(0\), 修改时暴力修改, 维护一个 tag, 表示这个区间内的数是否都小于等于 \(1\).

/*
  The code was written by yifan, and yifan is neutral!!!
 */

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

template<typename T>
inline T read() {
	T x = 0;
	bool fg = 0;
	char ch = getchar();
	while (ch < '0' || ch > '9') {
		fg |= (ch == '-');
		ch = getchar();
	}
	while (ch >= '0' && ch <= '9') {
		x = (x << 3) + (x << 1) + (ch ^ 48);
		ch = getchar();
	}
	return fg ? ~x + 1 : x;
}

const int N = 50010;

int n, len;
int be[N], le[510], re[510];
ll val[N], sum[510];
bool none[510];

void Reset(int x, int need) {
	if (none[x])	return ;
	none[x] = 1;
	sum[x] = 0;
	for (int i = le[x]; i <= re[x]; ++ i) {
		if (need) {
			val[i] = floor(sqrt(val[i]));
		}
		sum[x] += val[i];
		if (val[i] > 1) {
			none[x] = 0;
		}
	}
}

void kf(int l, int r) {
	int lim = min(re[be[l]], r);
	if (!none[be[l]]) {
		for (int i = l; i <= lim; ++ i) {
			val[i] = floor(sqrt(val[i]));
		}
		Reset(be[l], 0);
	}
	if (be[l] == be[r])	return ;
	if (!none[be[r]]) {
		for (int i = le[be[r]]; i <= r; ++ i) {
			if (none[be[r]])	break ;
			val[i] = floor(sqrt(val[i]));
		}
		Reset(be[r], 0);
	}
	for (int i = be[l] + 1; i <= be[r] - 1; ++ i) {
		if (none[i])	continue ;
		Reset(i, 1);
	}
}

int ask(int l, int r) {
	int lim = min(r, re[be[l]]), ans = 0;
	for (int i = l; i <= lim; ++ i) {
		ans += val[i];
	}
	if (be[l] == be[r])	return ans;
	for (int i = le[be[r]]; i <= r; ++ i) {
		ans += val[i];
	}
	for (int i = be[l] + 1; i <= be[r] - 1; ++ i) {
		if (none[i]) {
			ans += sum[i];
			continue ;
		}
		for (int j = le[i]; j <= re[i]; ++ j) {
			ans += val[j];
		}
	}
	return ans ;
}

int main() {
	n = read<int>(), len = sqrt(n);
	for (int i = 1; i <= n; ++ i) {
		val[i] = read<ll>();
		be[i] = (i - 1) / len + 1;
		le[be[i]] = re[be[i] - 1] + 1;
		re[be[i]] = min(n, be[i] * len);
		sum[be[i]] += val[i];
	}
	for (int i = 1, op, l, r, c; i <= n; ++ i) {
		op = read<int>(), l = read<int>(), r = read<int>(), c = read<int>();
		if (op == 0) {
			kf(l, r);
		}
		else {
			printf("%d\n", ask(l, r));
		}
	}
	return 0;
}

数列分块入门 6

单点插入, 单点询问.
好像也算是平衡树的操作吧, 我们这里将平时分块用的数组改为可以改变长度的 vector, 插入操作可以使用 insert 来实现, 随机数据下这样就可以过了, 非随机数据下, 如果一个块的长度太大了, 我们需要重构块.

/*
  The code was written by yifan, and yifan is neutral!!!
 */

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;

template<typename T>
inline T read() {
	T x = 0;
	bool fg = 0;
	char ch = getchar();
	while (ch < '0' || ch > '9') {
		fg |= (ch == '-');
		ch = getchar();
	}
	while (ch >= '0' && ch <= '9') {
		x = (x << 3) + (x << 1) + (ch ^ 48);
		ch = getchar();
	}
	return fg ? ~x + 1 : x;
}

const int N = 2e5 + 5;

int n, len;
ll val[N];
vector<int> kuai[510];

pii Find(int pos) {
	int k = 1;
	while (pos > (int)kuai[k].size()) {
		pos -= (int)kuai[k].size();
		++ k;
	}
	return {k, pos - 1};
}

void rebuild() {
	int cnt = 0;
	for (int i = 1; i <= len + 1; ++ i) {
		if (kuai[i].empty())	break;
		for (int v : kuai[i]) {
			val[++ cnt] = v;
		}
		kuai[i].clear();
	}
	len = sqrt(cnt);
	for (int i = 1; i <= cnt; ++ i) {
		kuai[(i - 1) / len + 1].emplace_back(val[i]);
	}
}

void Insert(int pos, int c) {
	pii it = Find(pos);
	kuai[it.first].insert(kuai[it.first].begin() + it.second, c);
	if ((int)kuai[it.first].size() >= (7 * len)) {
		rebuild();
	}
}

int main() {
	n = read<int>(), len = sqrt(n);
	for (int i = 1; i <= n; ++ i) {
		val[i] = read<ll>();
		kuai[(i - 1) / len + 1].emplace_back(val[i]);
	}
	for (int i = 1, op, l, r, c; i <= n; ++ i) {
		op = read<int>(), l = read<int>(), r = read<int>(), c = read<int>();
		if (op == 0) {
			Insert(l, r);
		}
		else {
			pii it = Find(r);
			cout << kuai[it.first][it.second] << '\n';
		}
	}
	return 0;
}

数列分块入门 7

区间加法 + 区间乘法 + 单点查询.
我们需要维护两个 tag, 要注意两个顺序, 先加后乘, 我们需要把加法 tag 里面的数也进行乘法, 先乘后加就不用, 再进行操作前都需要对块进行下放标记的操作.

/*
  The code was written by yifan, and yifan is neutral!!!
 */

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

template<typename T>
inline T read() {
	T x = 0;
	bool fg = 0;
	char ch = getchar();
	while (ch < '0' || ch > '9') {
		fg |= (ch == '-');
		ch = getchar();
	}
	while (ch >= '0' && ch <= '9') {
		x = (x << 3) + (x << 1) + (ch ^ 48);
		ch = getchar();
	}
	return fg ? ~x + 1 : x;
}

const int N = 1e5 + 5;
const int mod = 10007;

int n, len;
int be[N], le[510], re[510];
ll val[N], tag1[510], tag2[510];

inline void pushup(int x) {
	for (int i = le[x]; i <= re[x]; ++ i) {
		val[i] = (val[i] * tag2[x] + tag1[x]) % mod;
	}
	tag2[x] = 1, tag1[x] = 0;
}

void add(int l, int r, ll c) {
	int lim = min(r, re[be[l]]);
	pushup(be[l]);
	for (int i = l; i <= lim; ++ i) {
		val[i] = (val[i] + c) % mod;
	}
	if (be[l] == be[r])	return ;
	pushup(be[r]);
	for (int i = le[be[r]]; i <= r; ++ i) {
		val[i] = (val[i] + c) % mod;
	}
	for (int i = be[l] + 1; i <= be[r] - 1; ++ i) {
		tag1[i] = (tag1[i] + c) % mod;
	}
}

void times(int l, int r, ll c) {
	int lim = min(r, re[be[l]]);
	pushup(be[l]);
	for (int i = l; i <= lim; ++ i) {
		val[i] = val[i] * c % mod;
	}
	if (be[l] == be[r])	return ;
	pushup(be[r]);
	for (int i = le[be[r]]; i <= r; ++ i) {
		val[i] = val[i] * c % mod;
	}
	for (int i = be[l] + 1; i <= be[r] - 1; ++ i) {
		tag1[i] = tag1[i] * c % mod;
		tag2[i] = tag2[i] * c % mod;
	}
}

ll ask(int x) {
	return (val[x] * tag2[be[x]] + tag1[be[x]]) % mod;
}

int main() {
	n = read<int>(), len = sqrt(n);
	for (int i = 1; i <= n; ++ i) {
		val[i] = read<ll>();
		be[i] = (i - 1) / len + 1;
		tag2[be[i]] = 1;
		le[be[i]] = re[be[i] - 1] + 1;
		re[be[i]] = min(n, be[i] * len);
	}
	for (int i = 1, op, l, r, c; i <= n; ++ i) {
		op = read<int>(), l = read<int>(), r = read<int>(), c = read<int>();
		if (op == 0) {
			add(l, r, c);
		}
		else if (op == 1) {
			times(l, r, c);
		}
		else {
			printf("%lld\n", ask(r) % mod);
		}
	}
	return 0;
}

数列分块入门 8

dzy 说:“分块大佬不做 \(8\)\(9\)”, 我不是大佬, 所以, 我就做了 =_=||
区间修改和查询相同元素的个数, 有预处理操作, 对于不完整的块暴力修改, 完整的块打标记.

/*
  The code was written by yifan, and yifan is neutral!!!
 */

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

template<typename T>
inline T read() {
	T x = 0;
	bool fg = 0;
	char ch = getchar();
	while (ch < '0' || ch > '9') {
		fg |= (ch == '-');
		ch = getchar();
	}
	while (ch >= '0' && ch <= '9') {
		x = (x << 3) + (x << 1) + (ch ^ 48);
		ch = getchar();
	}
	return fg ? ~x + 1 : x;
}

const int N = 1e5 + 5;

int n, len;
int be[N], tag[510], le[510], re[510];
ll val[N];

void Reset(int x) {
	if (tag[x] == -1)	return ;
	for (int i = le[x]; i <= re[x]; ++ i) {
		val[i] = tag[x];
	}
	tag[x] = -1;
}

int work(int l, int r, int c) {
	int lim = min(r, re[be[l]]), ans = 0;
	Reset(be[l]);
	for (int i = l; i <= lim; ++ i) {
		ans += (val[i] == c);
		val[i] = c;
	}
	if (be[l] == be[r])	return ans;
	Reset(be[r]);
	for (int i = le[be[r]]; i <= r; ++ i) {
		ans += (val[i] == c);
		val[i] = c;
	}
	for (int i = be[l] + 1; i <= be[r] - 1; ++ i) {
		if (tag[i] == c) {
			ans += (re[i] - le[i] + 1);
		}
		else if (tag[i] == -1) {
			for (int j = le[i]; j <= re[i]; ++ j) {
				ans += (val[j] == c);
			}
		}
		tag[i] = c;
	}
	return ans;
}

int main() {
	n = read<int>(), len = sqrt(n);
	for (int i = 1; i <= n; ++ i) {
		val[i] = read<int>();
		be[i] = (i - 1) / len + 1;
		tag[be[i]] = -1;
		le[be[i]] = re[be[i] - 1] + 1;
		re[be[i]] = min(n, be[i] * len);
	}
	for (int i = 1, l, r, c; i <= n; ++ i) {
		l = read<int>(), r = read<int>(), c = read<int>();
		printf("%d\n", work(l, r, c));
	}
	return 0;
}

数列分块入门 9

区间中位数, 我不太会, 这个题被卡出阴影了.

/*
  The code was written by yifan, and yifan is neutral!!!
 */

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;

template<typename T>
inline T read() {
	T x = 0;
	bool fg = 0;
	char ch = getchar();
	while (ch < '0' || ch > '9') {
		fg |= (ch == '-');
		ch = getchar();
	}
	while (ch >= '0' && ch <= '9') {
		x = (x << 3) + (x << 1) + (ch ^ 48);
		ch = getchar();
	}
	return fg ? ~x + 1 : x;
}

const int N = 2e5 + 10;

int n, len, cnt;
int be[N], le[N], re[N], cot[N];
pii f[510][510];
ll val[N];
vector<int> s[N], num;

pii check(pii a, pii b) {
	if (b.first > a.first || (b.first == a.first && b.second < a.second)) {
		return b;
	}
	return a;
}

void pre(int x) {
	for (int i = 1; i <= n + 5; ++ i) {
		cot[i] = 0;
	}
	int mx = 0, ans = 0;
	for (int i = le[x]; i <= n; ++ i) {
		cot[val[i]] ++;
		pii it = check({mx, ans}, {cot[val[i]], val[i]});
		mx = it.first, ans = it.second;
		if (be[i] != be[i + 1]) {
			f[i][be[i]] = {mx, ans};
		}
	}
}

pii Find(int l, int r, int lr, int rr) {
	pii res;
	for (int i = l; i <= r; ++ i) {
		int num = val[i];
		int posl = lower_bound(s[num].begin(), s[num].end(), lr) - s[num].begin();
		int posr = upper_bound(s[num].begin(), s[num].end(), rr) - s[num].begin() - 1;
		res = check(res, {posr - posl + 1, val[i]});
	}
	return res;
}

int ask(int l, int r) {
	if (be[l] == be[r]) {
		return Find(l, r, l, r).second;
	}
	pii res = f[be[l] + 1][be[r] - 1];
	res = check(res, Find(l, re[be[l]], l, r));
	res = check(res, Find(le[be[r]], r, l, r));
	return res.second;
}

void init() {
	len = 200;
	for (int i = 1; i <= n; ++ i) {
		be[i] = (i - 1) / len + 1;
	}
	for (int i = 1; i <= be[n]; ++ i) {
		le[i] = (i - 1) * len + 1, re[i] = i * len;
	}
	re[be[n]] = n;
	for (int i = 1; i <= be[n]; ++ i) {
		for (int j = 0; j <= n + 10; ++ j) {
			cot[j] = 0;	
		}
		int maxcnt = 0, maxnum = 0;
		for (int j = le[i]; j <= n; ++ j) {
			cot[val[j]]++;
			pii it = check({maxcnt, maxnum}, {cot[val[j]], val[j]});
			maxcnt = it.first, maxnum = it.second;
			if (be[j] != be[j + 1]) {
				f[i][be[j]] = {maxcnt, maxnum};
			}
		}
	}
}

int main() {
	n = read<int>(), len = 200;
	for (int i = 1; i <= n; ++ i) {
		val[i] = read<ll>();
		num.emplace_back(val[i]);
	}
	sort(num.begin(), num.end());
	num.erase(unique(num.begin(), num.end()), num.end());
	for (int i = 1; i <= n; ++ i) {
		val[i] = lower_bound(num.begin(), num.end(), val[i]) - num.begin();
		s[val[i]].emplace_back(i);
	}
	init();
	for (int i = 1, l, r; i <= n; ++ i) {
		l = read<int>(), r = read<int>();
		if (l > r)	swap(l, r);
		printf("%d\n", num.at(ask(l, r)));
	}
	return 0;
}
posted @ 2023-07-05 21:21  yi_fan0305  阅读(42)  评论(0编辑  收藏  举报