2021牛客寒假算法基础集训营3 买礼物 (思维 + 线段树)

题目链接: 买礼物

大致题意

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;
}

END

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

逍遥Fau

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值