bzoj 4137:[FJOI2015]火星商店问题(离线线段树分治 + 可持久化 trie)

题目链接:火星商店问题
在这里插入图片描述


感觉就是HDU 5390 的加强版

询问是一段时间段,而更新是时间点

固定商品就是一棵可持久化 trie上区间查找。考虑新购买的商品:

在线做法:以时间建一棵线段树,每个节点维护一个可持久化 trie,通过线段树区间查询各个时间段来求得询问的答案。(也就是线段树套可持久化 trie 的做法)这么做空间必定裂开。

考虑离线做法:询问可以在线段树上分成多个子区间且互不干扰(也就是子问题),考虑在线段树上
离线分治:将询问操作的时间区间拆成 log 段,插入到对应的线段树上的结点中,更新操作的时间是一个点,从根节点一路插入到叶子结点。

遍历线段树的所有结点,对于线段树每个结点的操作序列:将更新操作按下标排序,依次维护到可持久化 trie 上,询问操作的下标离散化,在 当前维护的可持久化 trie 上查询即可。


代码:

#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e5 + 10;						//在洛谷上空间得开大点
int n,m,val[maxn],ans[maxn];
vector<int> t;									//用于离散化 
struct trie{									//可持久化字典树 
	int sz,son[maxn * 40][2],sum[maxn * 40],root[maxn];
	void init() {
		for(int i = 0; i <= sz; i++)
			root[i] = son[i][0] = son[i][1] = sum[i] = 0;
		sz = 0;
	}
	void upd(int &rt,int v) {					//类似主席树的更新,每一步都要更新一个结点 
		++sz;
		son[sz][0] = son[rt][0];
		son[sz][1] = son[rt][1];
		sum[sz] = sum[rt] + 1;
		rt = sz;
		int t = rt;
		for(int i = 19; i >= 0; i--) {
			int p = (v >> i) & 1;
			++sz;
			int s = son[t][p];
			sum[sz] = sum[s] + 1;
			son[sz][0] = son[s][0];
			son[sz][1] = son[s][1];
			son[t][p] = sz;
			t = son[t][p];
		}
	}
	int qry(int l,int r,int v) {
		if(l > r) return 0;
		int ans = 0;
		int L = root[l],R = root[r];
		for(int i = 19; i >= 0; i--) {
			int p = (v >> i) & 1;
			if(sum[son[R][p ^ 1]] - sum[son[L][p ^ 1]] > 0) {
				L = son[L][p ^ 1],R = son[R][p ^ 1];
				ans |= (1 << i);
			} else {
				L = son[L][p],R = son[R][p];
			}
		}	
		return ans;
	}
}tr1,tr2;
struct node {
	int id,op,a,b,v;
	node(int id = 0,int op = 0,int a = 0,int b = 0,int v = 0) {
		this -> id = id;
		this -> op = op;
		this -> a = a;
		this -> b = b;
		this -> v = v;
	}
	bool operator < (const node &rhs) const {
		if(op == rhs.op && op == 0) return a < rhs.a;
		return op < rhs.op;
	}
};
struct seg_tree {
	vector<node> opt[maxn << 2];
	#define lson rt << 1,l,mid
	#define rson rt << 1 | 1,mid + 1,r
	void build(int rt,int l,int r) {
		opt[rt].clear();
		if(l == r) return ;
		int mid = l + r >> 1;
		build(lson);build(rson);
	}
	void insert_upd(int p,node v,int rt,int l,int r) {
		opt[rt].push_back(v);
		if(l == r) return ;
		int mid = l + r >> 1;
		if(p <= mid) insert_upd(p,v,lson);
		else insert_upd(p,v,rson);
	}
	void insert_qry(int L,int R,node v,int rt,int l,int r) {
		if(L > R) return ;
		if(L <= l && r <= R) {
			opt[rt].push_back(v);
			return ;
		}
		int mid = l + r >> 1;
		if(L <= mid) insert_qry(L,R,v,lson);
		if(mid + 1 <= R) insert_qry(L,R,v,rson);
	}
	void solve(int rt,int l,int r) {
		t.clear();											//离散化 
		sort(opt[rt].begin(),opt[rt].end());
		for(auto it : opt[rt]) {
			if(it.op == 0)
				t.push_back(it.a);
		}
		int p = unique(t.begin(),t.end()) - t.begin();
		tr2.init();
		for(auto it : opt[rt]) {
			if(it.op == 0) {
				int tmp = lower_bound(t.begin(),t.begin() + p,it.a) - t.begin() + 1;	
				if(!tr2.root[tmp])
					tr2.root[tmp] = tr2.root[tmp - 1];
				tr2.upd(tr2.root[tmp],it.b);
			} else {
				int li = lower_bound(t.begin(),t.begin() + p,it.a) - t.begin() + 1;
				int ri = upper_bound(t.begin(),t.begin() + p,it.b) - t.begin();
				ans[it.id] = max(ans[it.id],tr2.qry(li - 1,ri,it.v));
			}
		}
		if(l == r) return ;
		int mid = l + r >> 1;
		solve(lson); solve(rson);
	}
}seg;
int main() {
	scanf("%d%d",&n,&m);
	tr1.init();
	vector<int> tmp;
	for(int i = 1; i <= n; i++) {
		scanf("%d",&val[i]);
		tr1.root[i] = tr1.root[i - 1];
		tr1.upd(tr1.root[i],val[i]);
	}
	seg.build(1,1,m);
	int tot = 0;
	for(int i = 1; i <= m; i++) {
		int op,a,b,v,d;
		scanf("%d",&op);
		if(op == 0) {
			scanf("%d%d",&a,&b);
			seg.insert_upd(++tot,node(i,0,a,b,0),1,1,m);
		} else {
			scanf("%d%d%d%d",&a,&b,&v,&d);
			ans[i] = tr1.qry(a - 1,b,v);
			seg.insert_qry(max(tot - d + 1,1),tot,node(i,1,a,b,v),1,1,m);
			tmp.push_back(i);
		}
	}
	seg.solve(1,1,m);
	for(auto it : tmp)
		printf("%d\n",ans[it]);
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值