P3293 [SCOI2016] 美味

经典题,\(\rm 01Trie\) 和 主席树的结合。

考虑一个没有偏移量的时候如何计算,其实就是一个裸的可持久化 \(\rm Trie\)

但是有了偏移量就不一样了,这会导致直接改变 \(\rm Trie\) 的结构,十分不好做。

套路的逐位考虑,从高位枚举到低位。假设当前找到的数为 \(\rm ret\),考虑到 \(i\) 位,则不难发现可以选择的值域区间为 \([ret - add, ret + 2 ^ {i + 1} - add]\)。其中前 \(2^i\) 个数 \(i\) 位为 \(0\) , 剩余为 \(1\).

\(\rm 01Trie\) 的思想,我们尽量往反向走,那么我们只需要查找反向区间中是否有数字,使用主席树即可。

code:

#include<bits/stdc++.h>
#define int long long 
using namespace std;

const int N = 2e5 + 10, M = 1e5;

struct Persist_Seg{
	#define mid (l + r >> 1)
	struct node{
		int ls, rs, sum;
	}hjt[N << 5];
	int root[N], cnt;
	void pushup(int o){hjt[o].sum = hjt[hjt[o].ls].sum + hjt[hjt[o].rs].sum;}	
	void add(int l, int r, int pre, int& now, int x){
		hjt[++cnt] = hjt[pre]; now = cnt;
		if(l == r){hjt[now].sum++; return;}
		if(x <= mid) add(l, mid, hjt[pre].ls, hjt[now].ls, x);
		else add(mid + 1, r, hjt[pre].rs, hjt[now].rs, x);
		pushup(now);
	}
	bool check(int l, int r, int Lo, int Ro, int s, int t){
		if(l > r) return false;
 		if(s <= l && r <= t) return hjt[Ro].sum - hjt[Lo].sum;
		bool ret = false;
		if(s <= mid) ret |= check(l, mid, hjt[Lo].ls, hjt[Ro].ls, s, t);
		if(mid < t) ret |= check(mid + 1, r, hjt[Lo].rs, hjt[Ro].rs, s, t);
		return ret; 
	}
}tr;

int n, m;

int solve(int pivot, int ad, int l, int r){
	int ret = 0;
	for(int i = 20; i >= 0; i--){
		int c = pivot & (1 << i);
		if(c && !tr.check(0, M, tr.root[l - 1], tr.root[r], ret - ad, ret - ad + (1 << i) - 1)) ret += (1 << i);
		else if(!c && tr.check(0, M, tr.root[l - 1], tr.root[r], ret - ad + (1 << i), ret - ad + (1 << i + 1) - 1)) ret += (1 << i); 
	}
	return ret ^ pivot;
}

signed main(){
	ios::sync_with_stdio(0);
	cin.tie(0); cout.tie(0);
	cin >> n >> m;
	for(int i = 1; i <= n; i++){
		int x; cin >> x;
		tr.add(0, M, tr.root[i - 1], tr.root[i], x);
	}
	for(int i = 1; i <= m; i++){
		int b, x, l, r;
		cin >> b >> x >> l >> r;
		cout << solve(b, x, l, r) << "\n";
	}
	
	return 0;
}
posted @ 2024-04-25 13:27  Little_corn  阅读(8)  评论(0编辑  收藏  举报