【bzoj2653】middle 可持久化线段树区间合并
题目描述
一个长度为n的序列a,设其排过序之后为b,其中位数定义为b[n/2],其中a,b从0开始标号,除法取下整。给你一个长度为n的序列s。回答Q个这样的询问:s的左端点在[a,b]之间,右端点在[c,d]之间的子序列中,最大的中位数。其中a<b<c<d。位置也从0开始标号。我会使用一些方式强制你在线。
输入
第一行序列长度n。接下来n行按顺序给出a中的数。
接下来一行Q。然后Q行每行a,b,c,d,我们令上个询问的答案是x(如果这是第一个询问则x=0)。
令数组q={(a+x)%n,(b+x)%n,(c+x)%n,(d+x)%n}。
将q从小到大排序之后,令真正的要询问的a=q[0],b=q[1],c=q[2],d=q[3]。
输入保证满足条件。
第一行所谓“排过序”指的是从大到小排序!
输出
Q行依次给出询问的答案。
样例输入
5
170337785
271451044
22430280
969056313
206452321
3
3 1 0 2
2 3 1 4
3 1 4 0
样例输出
271451044
271451044
969056313
题解
可持久化线段树区间合并(断句为:可持久化线段树/区间合并)
首先离散化,然后一眼二分,问题转化为:求是否存在一个左端点在 $[a,b]$ ,右端点在 $[c,d]$ 的区间,使得区间内大于等于 $mid$ 的数的个数 $\ge$ 小于 $mid$ 的数的个数。
如果把大于等于 $mid$ 的数看作 $+1$ ,小于 $mid$ 的数看作 $-1$ ,那么就是要找一段满足端点限制的区间,使得区间和大于等于 $0$ 。
也就是说求出满足条件的区间中最大区间和,判断其是否大于等于 $0$ 即可。
考虑怎么找出满足条件的区间中最大区间和:必选段为 $[b,c]$ ,剩下的是 $[a,b-1]$ 中包含右端点的最大连续子段和以及 $[c+1,d]$ 中包含左端点的最大连续子段和。
于是使用线段树区间合并来维护区间和、包含左&右端点的最大连续子段和。
但是不能对于每个二分的 $mid$ 维护一棵线段树。由于 $mid$ 从 $0$ 变化到 $n$ 的过程中,修改的总次数为 $n$ ,因此可以对每一个权值 $mid$ 开一棵可持久化线段树来维护。
于是这就是可持久化线段树区间合并?
时间复杂度 $O(n\log^2 n)$
PS:不需要从大到小排序.. 代码瞎写的..
#include <cstdio> #include <algorithm> #define N 20010 #define lson l , mid , ls[x] #define rson mid + 1 , r , rs[x] using namespace std; struct node { int w , id; bool operator<(const node &a)const {return w > a.w;} }v[N]; struct data { int ts , lv , rv; data() {ts = lv = rv = 0;} friend data operator+(const data &a , const data &b) { data ans; ans.ts = a.ts + b.ts; ans.lv = max(a.lv , a.ts + b.lv); ans.rv = max(b.rv , b.ts + a.rv); return ans; } }a[N * 50]; int p[4] , ref[N] , top , ls[N * 50] , rs[N * 50] , tot , root[N]; inline void pushup(int x) { a[x] = a[ls[x]] + a[rs[x]]; } void build(int l , int r , int &x) { x = ++tot; if(l == r) { a[x].ts = -1 , a[x].lv = a[x].rv = 0; return; } int mid = (l + r) >> 1; build(lson) , build(rson); pushup(x); } void update(int p , int l , int r , int x , int &y) { y = ++tot; if(l == r) { a[y].ts = a[y].lv = a[y].rv = 1; return; } int mid = (l + r) >> 1; if(p <= mid) rs[y] = rs[x] , update(p , lson , ls[y]); else ls[y] = ls[x] , update(p , rson , rs[y]); pushup(y); } data query(int b , int e , int l , int r , int x) { if(b <= l && r <= e) return a[x]; int mid = (l + r) >> 1; if(e <= mid) return query(b , e , lson); else if(b > mid) return query(b , e , rson); else return query(b , e , lson) + query(b , e , rson); } int main() { int n , m , i , l , r , mid , ans = 0; scanf("%d" , &n); for(i = 1 ; i <= n ; i ++ ) scanf("%d" , &v[i].w) , v[i].id = i; sort(v + 1 , v + n + 1); build(1 , n , root[0]); for(i = 1 ; i <= n ; i ++ ) { if(v[i].w != ref[top]) ref[++top] = v[i].w , root[top] = root[top - 1]; update(v[i].id , 1 , n , root[top] , root[top]); } scanf("%d" , &m); while(m -- ) { for(i = 0 ; i < 4 ; i ++ ) scanf("%d" , &p[i]) , p[i] = (p[i] + ans) % n + 1; sort(p , p + 4); l = 1 , r = top; while(l <= r) { mid = (l + r) >> 1; if(query(p[1] , p[2] , 1 , n , root[mid]).ts + query(p[0] , p[1] - 1 , 1 , n , root[mid]).rv + query(p[2] + 1 , p[3] , 1 , n , root[mid]).lv >= 0) ans = ref[mid] , r = mid - 1; else l = mid + 1; } printf("%d\n" , ans); } return 0; }