P9991 [Ynoi Easy Round 2023] TEST_107 题解

先考虑暴力 O(nm)O(nm) 怎么做。

我们肯定是把区间中某一个数全部删掉。维护 fif_igig_i 分别表示 ii 前面第一个和 aia_i 相等的位置和后面第一个。不存在时,fi=0,gi=n+1f_i=0,g_i=n+1

考虑 i[l,r]i \in [l,r],显然要删掉 aia_i,答案为 ifi1i-f_i-1。但是注意到 fi<lf_i < l 时不用删,这时变成 ili-l

此外每个区间中每个不同的数最后一个出现位置有 gi>rg_i > r,此时有 rir-i 的贡献。

于是我们只需要处理三部分:

  1. 对于所有 filf_i \geq l,求 ifi1i-f_i - 1 最大值。
  2. 对于 fi<lf_i < l,求 ili-l 最大值。
  3. 对于 gi>rg_i > r,求 rir-i 最大值。

三个答案取最大值即为答案。

先考虑第一个。可以离线枚举 ll,用线段树维护单点修改区间 max\max

后面两个类似,以第二个为例。我们对于每个区间要求最大的 ii 使得 fi<lf_i < l。考虑用 ST 表,然后二分位置,询问区间 ff 最小值。

于是就做完了,时间复杂度 O(nlogn)O(n \log n),空间复杂度 O(nlogn)O(n \log n)。然而很卡常,需要使用 basic_string 替代 vector 并且开快读和各种优化。感觉这个做法其实不很优美。

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <string>
#include <vector>
#include <string>
using namespace std;

const int N = 2e6 + 5;

int lstpos[N];
namespace FastIO {
	char* p1, * p2, buf[1 << 14];
#define getchar() (p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, (1 << 14), stdin), p1 == p2) ? EOF : *p1++)

	template <typename T>
	inline void read(T& x)
	{
		x = 0;
		char ch = getchar();
		while (ch < '0' || ch > '9') {
			ch = getchar();
		}
		while (ch >= '0' && ch <= '9') {
			x = (x << 1) + (x << 3) + (ch ^ 48);
			ch = getchar();
		}
	}

	template <typename T>
	void write(T x) {
		if (x < 0) {
			putchar('-');
			x = -x;
		}
		if (x > 9) write(x / 10);
		putchar(x % 10 ^ 48);
	}

	template <typename T>
	inline void writeln(T x, char sep = '\n') {
		write(x);
		putchar(sep);
	}
}

using namespace FastIO;

int n, m;
int a[N], g[N];
int gpos[N];
int LG2[N];

class ST
{
public:
	int f[22][N];
	bool flag;
	void Init(int* a)
	{
		if (flag) memset(f, 0x3f, sizeof f);
		else memset(f, 0, sizeof f);
		for (int i = 1; i <= n; i++) f[0][i] = a[i];
		for (int j = 1; j <= 21; j++)
		{
			for (int i = 1; i + (1 << j) - 1 <= n; i++)
			{
				if (flag) f[j][i] = min(f[j - 1][i], f[j - 1][i + (1 << (j - 1))]);
				else f[j][i] = max(f[j - 1][i], f[j - 1][i + (1 << (j - 1))]);
			}
		}
	}
	int query(const int& l, const int& r)
	{
		int p(LG2[r - l + 1]);
		if (flag) return min(f[p][l], f[p][r - (1 << p) + 1]);
		return max(f[p][l], f[p][r - (1 << p) + 1]);
	}
}s1;

int ans[N];
int L[N], R[N];
basic_string<int> v[N];
int l, r, x;

class SegmentTree
{
public:
	struct Node
	{
		int l, r, maxn;
	}tr[N << 2];
	void build(int u, int l, int r, int* a)
	{
		tr[u] = { l, r, a[l] };
		if (l == r) return;
		int mid = l + r >> 1;
		build(u << 1, l, mid, a);
		build(u << 1 | 1, mid + 1, r, a);
		tr[u].maxn = max(tr[u << 1].maxn, tr[u << 1 | 1].maxn);
	}
	void update(int u)
	{
		if (tr[u].l == tr[u].r)
		{
			tr[u].maxn = 0;
			return;
		}
		int mid = tr[u].l + tr[u].r >> 1;
		if (x <= mid) update(u << 1);
		else update(u << 1 | 1);
		tr[u].maxn = max(tr[u << 1].maxn, tr[u << 1 | 1].maxn);
	}
	int query(int u)
	{
		if (tr[u].l >= l and tr[u].r <= r) return tr[u].maxn;
		int res(0), mid(tr[u].l + tr[u].r >> 1);
		if (l <= mid) res = query(u << 1);
		if (r > mid) res = max(res, query(u << 1 | 1));
		return res;
	}
}sgt;

int b[N];

basic_string<int> place[N];

int main()
{
	for (int i = 2; i < N; i++) LG2[i] = LG2[i / 2] + 1;
	read(n), read(m);
	for (int i = 1; i <= n; i++)
	{
		read(a[i]);
		lstpos[i] = g[a[i]];
		place[lstpos[i]] += i;
		g[a[i]] = i;
		b[i] = i - lstpos[i] - 1;
	}
	memset(g, 0, sizeof g);
	for (int i = n; i >= 1; i--)
	{
		gpos[i] = (g[a[i]] ? g[a[i]] : n + 1);
		g[a[i]] = i;
	}
	s1.flag = 1;
	s1.Init(lstpos);
	for (int i = 1; i <= m; i = -~i)
	{
		int l, r;
		read(l), read(r);
		L[i] = l, R[i] = r;
		v[l] += i;
		int nl = l, nr = r, pos = -1;
		while (nl <= nr)
		{
			int mid(nl + nr >> 1);
			if (s1.query(mid, r) < l)
			{
				pos = mid;
				nl = mid + 1;
			}
			else
			{
				nr = mid - 1;
			}
		}
		ans[i] = max(ans[i], pos - l);
	}
	s1.flag = 0;
	s1.Init(gpos);
	for (int i = 1; i <= m; i = -~i)
	{
		int l = L[i], r = R[i];
		int nl = l, nr = r, pos = n + 1;
		while (nl <= nr)
		{
			int mid((nl + nr) >> 1);
			if (s1.query(l, mid) > r)
			{
				pos = mid;
				nr = mid - 1;
			}
			else
			{
				nl = mid + 1;
			}
		}
		ans[i] = max(ans[i], r - pos);
	}
	sgt.build(1, 1, n, b);
	int lpos = 0;
	for (int i = n; i >= 0; i--)
	{
		if (v[i].size())
		{
			lpos = i;
			break;
		}
	}
	for (int i = 0; i <= lpos; i++)
	{
		l = i;
		for (auto& id : v[i])
		{
			int r = R[id];
			::r = r;
			ans[id] = max(ans[id], sgt.query(1));
		}
		for (auto& p : place[i])
		{
			::x = p;
			sgt.update(1);
		}
	}
	for (int i = 1; i <= m; i++)
	{
		writeln(ans[i]);
	}
	return 0;
}
posted @   HappyBobb  阅读(9)  评论(0编辑  收藏  举报  
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!
点击右上角即可分享
微信分享提示