题目链接: 买礼物
大致题意
QAQ
解题思路
对于这种一看就是DS且带修改的题, 我们通常可以先忽略修改操作. 先只考虑查询操作.
查询操作: 查询[l, r]是否有两个相同的数字, 我们可以维护一个last数组, 记录上一个与当前编号相同的位置. 这样我们对于每次查询, 我们可以遍历[l, r]的last数组, 看看是否存在一个last[i], 使得last[i]大于l.
显然这样太暴力了, 我们其实想询问在[l, r]区间的last数组中的最大值是否大于l即可. 线段树
很显然, 我们用线段树维护区间最大值即可. 对于每个查询, 我们询问[l, r]的最大值是否大于l即可.
现在考虑修改操作, 每次修改相当于删除了位置x的编号. 影响的位置有x和x的后继.
因为我们把x位置的编号删除了, 则last[x]就不存在了. 同理, x的后继位置(不妨记为next[x]), last[next[x]]应该变为last[x].
于是我们经过分析, 我们发现加上了修改操作, 也可以用线段树实现, 本质是 线段树的单点修改.
于是我们就可以快乐ac啦~
AC代码
#include <bits/stdc++.h>
#define rep(i, n) for (int i = 1; i <= (n); ++i)
using namespace std;
typedef long long ll;
const int N = 5E5 + 10, M = 1E6 + 10;
int a[N]; int last[M]; //a数组记录每个位置的编号
pair<int, int> pos[N]; //记录每个位置的前驱和后继 (因为修改的时候要用到后继)
int w[N]; //建树的数组, 记录每个位置初始的前驱 (相当于pos[i].first)
struct node {
int l, r;
int val;
}t[N << 2];
void pushup(int x) { t[x].val = max(t[x << 1].val, t[x << 1 | 1].val); }
void build(int l, int r, int x = 1) {
t[x] = { l, r, w[l] };
if (l == r) return;
int mid = l + r >> 1;
build(l, mid, x << 1), build(mid + 1, r, x << 1 | 1);
pushup(x);
}
void modify(int a, int c, int x = 1) {
if (t[x].l == t[x].r) {
t[x].val = c;
return;
}
int mid = t[x].l + t[x].r >> 1;
modify(a, c, x << 1 | (a > mid));
pushup(x);
}
int ask(int l, int r, int x = 1) {
if (l <= t[x].l and r >= t[x].r) return t[x].val;
int mid = t[x].l + t[x].r >> 1;
int res = 0;
if (l <= mid) res = ask(l, r, x << 1);
if (r > mid) res = max(res, ask(l, r, x << 1 | 1));
return res;
}
int main()
{
int n, m; cin >> n >> m;
rep(i, n) {
scanf("%d", &a[i]);
w[i] = pos[i].first = last[a[i]];
last[a[i]] = i;
}
memset(last, 0, sizeof last);
for (int i = n; i >= 1; --i) {
pos[i].second = last[a[i]];
last[a[i]] = i;
}
build(1, n);
rep(i, m) {
int tp; scanf("%d", &tp);
if (tp == 1) {
int x; scanf("%d", &x);
modify(x, 0);
auto& [pv, nt] = pos[x];
if (nt) modify(nt, pv); //如果不存在后继就没必要修改了
pos[pv].second = nt, pos[nt].first = pv;
}
else {
int l, r; scanf("%d %d", &l, &r);
int index = ask(l, r);
puts(index >= l ? "1" : "0");
}
}
return 0;
}