【BZOJ 4571】【SCOI 2016】美味

http://www.lydsy.com/JudgeOnline/problem.php?id=4571
这道题因为有加法,不能像可持久化trie那样每次判断只判断一个子树,而是在主席树上查询(log n)个子树。
从高位到低位依次确定(a_j+x_i)的二进制位。
注意(a_j+x_i)不是(10^5)级别的,是两倍大小。
时间复杂度(O(nlog n+mlog^2n))

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;

const int N = 200003;

struct node {
	int s, l, r;
} T[N * 20];

int cnt = 0, root[N], n, m;

void update(int &pos, int l, int r, int key) {
	T[++cnt] = T[pos]; pos = cnt; ++T[pos].s;
	if (l == r) return;
	int mid = (l + r) >> 1;
	if (key <= mid) update(T[pos].l, l, mid, key);
	else update(T[pos].r, mid + 1, r, key);
}

int Q(int TL, int TR, int l, int r, int L, int R) {
	if (L <= l && r <= R) return T[TR].s - T[TL].s;
	if (T[TR].s - T[TL].s == 0) return 0;
	int mid = (l + r) >> 1, s = 0;
	if (L <= mid) s += Q(T[TL].l, T[TR].l, l, mid, L, R);
	if (R > mid) s += Q(T[TL].r, T[TR].r, mid + 1, r, L, R);
	return s;
}

void solve(int b, int x, int TL, int TR) {
	int v, num = 0, numl, numr;
	for (int i = 17; i >= 0; --i) {
		v = (b >> i) & 1 ^ 1;
		numl = num | (v << i); numr = numl | (1 << i) - 1;
		if (Q(TL, TR, 0, 99999, numl - x, numr - x))
			num |= (v << i);
		else
			num |= ((v ^ 1) << i);
	}
	printf("%d
", num ^ b);
}

int main() {
	scanf("%d%d", &n, &m);
	int ai;
	for (int i = 1; i <= n; ++i) {
		scanf("%d", &ai);
		root[i] = root[i - 1];
		update(root[i], 0, 99999, ai);
	}
	
	int bi, xi, l, r;
	for (int i = 1; i <= m; ++i) {
		scanf("%d%d%d%d", &bi, &xi, &l, &r);
		solve(bi, xi, root[l - 1], root[r]);
	}
	return 0;
}
原文地址:https://www.cnblogs.com/abclzr/p/6590911.html