杭电多校第4场08题。
主席树+二分
看到题目应该是用主席树。当时看到k最大只有169,就想着先找到p在区间内的排名,然后暴力查找,写了好久,代码有点难调,交上去就是RE,应该是WA了,导致异或那步错了,进而导致RE。
题解是主席树加二分,二分答案mid,然后用主席树查询区间l-r内,值域在[p-mid,p+mid]内的点的个数,如果个数大于k个,说明我们的mid选大了,然后就要减小mid,否则增大mid,二分即可。
#includeusing namespace std; #define INF 0x3f3f3f3f const int N = 1e6 + 10; const int mod = 1e9 + 7; const int M = 1e6 + 10; //const int mx = 1e6; struct tree{ int cnt, lc, rc; }T[(M << 6)]; int root[N], a[N], b[N]; int n, m, q, tot; void build(int &rt, int l, int r)//建立一颗空树 { rt = ++tot; T[rt].cnt = 0; T[rt].lc = T[rt].rc = 0; if (l == r) return; int mid = (l + r) >> 1; build(T[rt].lc, l, mid); build(T[rt].rc, mid + 1, r); } void insert(int l, int r, int &cur, int pre, int pos) { T[++tot] = T[pre]; cur = tot; T[cur].cnt++; if (l == r) return; int mid = (l + r) >> 1; if (pos <= mid) insert(l, mid, T[cur].lc, T[pre].lc, pos); else insert(mid + 1, r, T[cur].rc, T[pre].rc, pos); } int query(int l, int r, int x, int y, int k) { if (r <= k) return T[y].cnt - T[x].cnt; if (l == r){ return T[y].cnt - T[x].cnt; } int s = T[T[y].lc].cnt - T[T[x].lc].cnt; int mid = (l + r) >> 1; if (k <= mid){ return query(l, mid, T[x].lc, T[y].lc, k); } else{ return s + query(mid + 1, r, T[x].rc, T[y].rc, k); } } int max(int a, int b) { return a > b ? a : b; } int main() { int T; scanf("%d", &T); for (int t = 1; t <= T; t++) { int mx = 0; scanf("%d%d", &n, &m); for (int i = 1; i <= n; i++) scanf("%d", &a[i]),mx=max(mx,a[i]); tot = 0; build(root[0], 1, mx); for (int i = 1; i <= n; i++) insert(1, mx, root[i], root[i - 1], a[i]); int lst=0; while (m--) { int l, r, p, k; scanf("%d%d%d%d", &l, &r, &p, &k); l ^= lst, r ^= lst, k ^= lst, p ^= lst; int L=0, R = 1e6; while (L < R) { int mid = (L + R) >> 1; int cnt = query(1, mx, root[l - 1], root[r], p + mid); if (p - mid - 1>0) cnt -= query(1, mx, root[l - 1], root[r], p - mid - 1); if (cnt >= k) R = mid; else L = mid + 1; } printf("%d\n", lst = L); } } return 0; }