题意
给定 $n$ 个数,接下来有 $q$ 次询问,每个询问的 $l, r, p, k$ 要异或上一次的答案,才是真正的值(也就是强制在线)。每次询问,输出 $[l, r]$ 内第 $k$ 小的 $|p-a[i]|$.
分析
通常主席树用来求区间第K大,其实它的实际作用是统计某个区间内值的个数。所以,
对于每次询问,对答案进行二分,对于可能的答案 $x$,对 $R_l \sim R_r$ 的线段树查找 $[p-x, p+x]$ 的是否为 $k$.
主席树中在值上建立的,这题数据范围为 $10^6$,不需要离散化(话说强制在线的离散化我也不会)
感觉有些卡常,32倍、55倍的空间都TLE,改成64倍就过了(为啥啊)
#includeusing namespace std; const int maxn = 1e5 + 100; int n, m; //int a[maxn]; //int rt[maxn], lc[maxn << 5], rc[maxn << 5], sum[maxn << 5]; //rt:不同版本的根节点 lc/rc: 左儿子、右儿子(公用) sum: 和(公用) int rt[maxn], lc[maxn*64], rc[maxn*64], sum[maxn*64]; int node_cnt; //node总计数, pnt_disc: A中数字对应B中的值 int range = 1000000; //数据范围,也就是线段树的大小 void build(int& last_node, int l, int r) { last_node = ++ node_cnt; sum[last_node] = 0; if(l == r) return; int mid = (l + r) >> 1; build(lc[last_node], l, mid); build(rc[last_node], mid+1, r); } int modify(int pre_rt, int v, int l, int r) { int new_rt = ++node_cnt; lc[new_rt] = lc[pre_rt]; rc[new_rt] = rc[pre_rt]; sum[new_rt] = sum[pre_rt] + 1; int mid = (l + r) >> 1; if(l == r) return new_rt; if(mid >= v) lc[new_rt] = modify(lc[new_rt],v, l, mid); else rc[new_rt] = modify(rc[new_rt], v, mid+1, r); return new_rt; } //查询[ql, qr]中不同元素个数 int query(int rt1, int rt2, int ql, int qr, int l, int r) { //printf("rt1:%d rt2:%d k:%d l:%d r:%d ", rt1, rt2, k, l, r); if(ql <= l && r <= qr) return sum[rt2]-sum[rt1]; int mid = (l + r) >> 1; int ans = 0; if(ql <= mid) ans += query(lc[rt1], lc[rt2], ql, qr, l, mid); if(qr > mid) ans += query(rc[rt1], rc[rt2], ql, qr, mid+1, r); return ans; } void print_debug() { printf("node_cnt: %d\n", node_cnt); for(int i = 0;i <= node_cnt;i++) printf("%d lc:%d rc:%d sum:%d\n", i, lc[i], rc[i], sum[i]); } int main() { int T; scanf("%d", &T); while(T--) { scanf("%d%d", &n, &m); node_cnt = 0; build(rt[0], 1, range); for(int i = 1;i <= n;i++) { int tmp; scanf("%d", &tmp); rt[i] = modify(rt[i-1], tmp, 1, range); //只在上一个版本的基础上修改 } int ans = 0; for(int i = 0;i ) { int l, r, p, k; scanf("%d%d%d%d", &l, &r, &p, &k); l ^= ans; r ^= ans; p ^= ans; k ^= ans; if(l > r) swap(l, r); int L = 0, R = range; // while(L <= R) { int M = L + (R-L)/2; if(query(rt[l-1], rt[r], max(1, p-M), min(p+M, range), 1, range) >= k) { ans = M; R = M-1; } else L = M+1; } printf("%d\n", ans); } } } [ Copy to Clipboard ] [ Save to File]
参考链接:
1. https://blog.csdn.net/birdmanqin/article/details/97964662
2. http://morecoder.com/article/1254619.html