bzoj 2653 洛谷 P2839 [国家集训队] middle

2653: middle

Time Limit: 20 Sec  Memory Limit: 512 MB
Submit: 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

Sample Output

271451044
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\n",ans);
    }
}

 

posted @ 2018-08-20 20:57  阿澈说他也想好好学习  阅读(152)  评论(0编辑  收藏  举报