2653: middle
Time Limit: 20 Sec Memory Limit: 512 MBSubmit: 2381 Solved: 1340
[Submit][Status][Discuss]
Description
一个长度为n的序列a,设其排过序之后为b,其中位数定义为b[n/2],其中a,b从0开始标号,除法取下整。给你一个
长度为n的序列s。回答Q个这样的询问:s的左端点在[a,b]之间,右端点在[c,d]之间的子序列中,最大的中位数。
其中a<b<c<d。位置也从0开始标号。我会使用一些方式强制你在线。
Input
第一行序列长度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]。
输入保证满足条件。
第一行所谓“排过序”指的是从小到大排序!
n<=20000,Q<=25000
Output
Q行依次给出询问的答案。
Sample Input
5
170337785
271451044
22430280
969056313
206452321
3
3 1 0 2
2 3 1 4
3 1 4 0
170337785
271451044
22430280
969056313
206452321
3
3 1 0 2
2 3 1 4
3 1 4 0
Sample Output
271451044
271451044
969056313
271451044
969056313
emmm这道题就是一道二分答案加主席树 二分我的中位数是什么 然后check判断他是否满足条件
那么怎么判断呢 对于一段区间 我们假设现在我们要check的中位数是x 对应这个区间我们建立一棵线段树
若一个数 ≥ x 那么这个数在对应线段树里面的值设为1 否则设为 - 1
那么对应这一段的区间和如果等于0 那么这个数就可以作为这段区间的中位数
如果 > 0 则表示这段区间内比他大的数偏多 也就是说我们现在枚举的这个数相对于中位数偏小 反之偏大
那么很显然这个东西是满足二分的
但是又出现了一个问题 就是我们怎么搞每一个数对应的区间的值吧 肯定不可能是对于每一个数都开一棵线段树
但是肯定不可能 时间空间都不够用 这时候就想到了主席树
然而这个主席树是怎么建立的呢 我们先对应所有的值都排一边序(要存储他们的原位置)
那么对于i + 1这个位置上的数对应的主席树 他相对于 i 这个位置上的数对应的主席树
发生的变化是不是就是将原数组中 i 对应的位置上的值从 1 改成 - 1 所以就很容易维护了
那么怎么对于[a,b] [c,d]查询呢 按照题意 [b,c]是必须选的 求区间和即可那么要使中位数最大 就要使我们所求的区间和尽可能大
也就是求从b起向左的最大区间和 和从c起向右的最大区间和 求个和 根据上述判断方式二分
然后就这样 我wa了好几次 竟然是查询右儿子的时候写的是 l 到 mid 丢脸...
代码
#include <bits/stdc++.h> using namespace std; const int N = 20000 + 10; int a,b,c,d,T,n,w[N],sum[32 * N],ls[32 * N],rs[32 * N]; int rmax[32 * N],lmax[32 * N],rt,root[N],q[10],ans = 0; struct node{ int val,pos; }s[N]; void update(int nd) { sum[nd] = sum[ls[nd]] + sum[rs[nd]]; rmax[nd] = max(rmax[rs[nd]],sum[rs[nd]] + rmax[ls[nd]]); lmax[nd] = max(lmax[ls[nd]],sum[ls[nd]] + lmax[rs[nd]]); } int build(int l,int r) { int nd = ++ rt; if(l == r) { sum[nd] = lmax[nd] = rmax[nd] = 1; return nd; } int mid = (l + r) >> 1; ls[nd] = build(l,mid); rs[nd] = build(mid + 1,r); update(nd); return nd; } int modify(int pre,int l,int r,int pos) { int nd = ++ rt; sum[nd] = sum[pre]; ls[nd] = ls[pre],rs[nd] = rs[pre]; if(l == r) { sum[nd] = -1; rmax[nd] = 0; lmax[nd] = 0; return nd; } int mid = (l + r) >> 1; if(pos <= mid) ls[nd] = modify(ls[pre],l,mid,pos); else rs[nd] = modify(rs[pre],mid + 1,r,pos); update(nd); return nd; } bool cmp(const node & a,const node & b) { return a.val < b.val; } int query(int nd,int l,int r,int L,int R) { if(l > R || r < L) return 0; if(l >= L && r <= R) return sum[nd]; int mid = (l + r) >> 1,ans = 0; if(L <= mid) ans += query(ls[nd],l,mid,L,R); if(mid < R) ans += query(rs[nd],mid + 1,r,L,R); return ans; } int query_r(int nd,int l,int r,int L,int R) { if(l > R || r < L) return 0; if(l >= L && r <= R) return rmax[nd]; int mid = (l + r) >> 1,ans = 0; if(mid < R) ans = query_r(rs[nd],mid + 1,r,L,R); if(L <= mid) { int l_rmax = query_r(ls[nd],l,mid,L,R); int r_sum = query(ls[nd],mid + 1,r,L,R); ans = max(ans,l_rmax + r_sum); } return ans; } int query_l(int nd,int l,int r,int L,int R) { if(L > R) return 0; if(l >= L && r <= R) return lmax[nd]; int mid = (l + r) >> 1,ans = 0; if(L <= mid) ans = query_l(ls[nd],l,mid,L,R); if(mid < R) { int r_lmax = query_l(ls[nd],mid + 1,r,L,R); int l_sum = query(ls[nd],l,mid,L,R); ans = max(ans,r_lmax + l_sum); } return ans; } bool check(int mid) { int ab = query_r(root[mid],1,n,q[1],q[2] - 1); int bc = query(root[mid],1,n,q[2],q[3]); int cd = query_l(root[mid],1,n,q[3] + 1,q[4]); if(ab + bc + cd >= 0) return true; return false; } int find( ) { int l = 1,r = n,ans = 0; while(l <= r) { int mid = (l + r) >> 1; if(check(mid)) ans = mid,l = mid + 1; else r = mid - 1; } return ans; } int main( ) { scanf("%d",& n); for(int i = 1;i <= n;i ++) { scanf("%d",& w[i]); s[i].pos = i; s[i].val = w[i]; } sort(s + 1,s + n + 1,cmp); scanf("%d",& T); root[1] = build(1,n); for(int i = 2;i <= n;i ++) { root[i] = modify(root[i - 1],1,n,s[i - 1].pos); } while(T --) { scanf("%d%d%d%d",& a,& b,& c,& d); q[1] = (a + ans) % n + 1; q[2] = (b + ans) % n + 1; q[3] = (c + ans) % n + 1; q[4] = (d + ans) % n + 1; sort(q + 1,q + 5); ans = s[find( )].val; printf("%d ",ans); } }