洛谷·[FJOI2015]火星商店问题

初见安~这里是传送门:洛谷P4585 [FJOI2015]火星商店问题

题解

听说是分治线段树套可持久化trie……蒟蒻不会分治线段树,就写了一个线段树套trie。

题意就是:n个初始为空的集合,m个操作。操作有:向某集合中加入一个整数或查询最近d次向编号在[l, r]中的集合加入的元素中,与x异或的最大值。

我们可以发现,对于询问操作有一个区间限制[l, r]和一个时间限制最近d次,再加上求最大异或值。三层限制就不能单纯用一个可持久化数据结构了,要树套树。因为是求异或最大值,我们很容易想到01trie,这一层解决了。接下来是区间和时间限制。区间我们似乎可以用线段树来框定。所以就是——开一棵线段树,区间[l, r]表示区间内的集合,线段树上每个点都开一棵01trie维护下方子节点内的信息。最后的时间限制,我们在每一个trie节点都记录一下最近一次更新的时间,若在d之前则不继续搜即可。

时间复杂度大概是O(mlog^2n)。空间复杂度因为初始全空,若加入一个数值,则线段树上对应的叶子节点到根节点路径上的所有点的01trie都要加入这个数。也就是说加入一个数需要log^2n个点,空间复杂度是O(nlog^2n)

上代码——

#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<queue>
#define maxn 40000007
using namespace std;
const int INF = 1e9;
typedef long long ll;
int read() {
	int x = 0, f = 1, ch = getchar();
	while(!isdigit(ch)) {if(ch == '-') f = -1; ch = getchar();}
	while(isdigit(ch)) x = (x << 1) + (x << 3) + ch - '0', ch = getchar();
	return x * f;
}

int n, m, tot;
struct trie {
	int t[maxn][2], mx[maxn];//因为要动态开点,所以全部放一起比较好
	void add(int p, int x, int d) {
		for(int i = 20; i >= 0; i--) {
			register int c = x >> i & 1;
			if(!t[p][c]) t[p][c] = ++tot; p = t[p][c];
			mx[p] = max(mx[p], d);
		}	
	}
	int ask(int p, int x, int d, int res) {//查询最大异或值
		for(int i = 20; i >= 0; i--) {
			register int c = x >> i & 1;
			if(t[p][c ^ 1] && mx[t[p][c ^ 1]] >= d) p = t[p][c ^ 1], res += (1 << i);
			else if(mx[t[p][c]] >= d) p = t[p][c];
			else return res;
		}
		return res;
	}
}T;

void insert(int p, int l, int r, int k, int x, int d) {//加入线段树
	T.add(p, x, d);//加入01trie
	if(l == r) return;
	register int mid = l + r >> 1;
	if(k <= mid) insert(p << 1, l, mid, k, x, d);
	else insert(p << 1 | 1, mid + 1, r, k, x, d);
}

int query(int p, int l, int r, int ls, int rs, int x, int d) {//线段树上区间查询
	if(ls <= l && r <= rs) return T.ask(p, x, d, 0);//套进01trie求最大值
	register int mid = l + r >> 1, ans = 0;
	if(ls <= mid) ans = max(ans, query(p << 1, l, mid, ls, rs, x, d));
	if(rs > mid) ans = max(ans, query(p << 1 | 1, mid + 1, r, ls, rs, x, d));
	return ans;
}

signed main() {
	n = read(), m = read(); tot = n << 2;
	for(int x, i = 1; i <= n; i++) x = read(), insert(1, 1, n, i, x, INF);
	
	register int day = 0, op, l, r, x, d;
	while(m--) {
		op = read();
		if(!op) x = read(), d = read(), day++, insert(1, 1, n, x, d, day);
		else l = read(), r = read(), x = read(), d = read(), printf("%d\n", query(1, 1, n, l, r, x, max(0, day - d + 1)));
	}
	return 0;
}

迎评:)
——End——

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值