zoukankan      html  css  js  c++  java
  • bzoj 2653: middle (主席树+二分)

    2653: middle

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

    HINT

     

    Source

    思路:

    这道题比较容易想到二分答案,但是check答案比较难想

    我们考虑check某个数的时候把大于它的值设为1,小于它的设为-1,那么对【b+1,c-1】区间取和,对【a,b】取右端最大,对【c,d】取左端最大加来如果大于等于0,那么说明值在右边,往右边二分否则往左边二分,

    但是如果我们每次check重新建一遍线段树,那肯定是会超时的,我们用主席树存就好了,我们先将主席树上各个点的值赋为1,然后依次输入2-n,每次输入将前一个数赋值为-1(因为排完序后前一个数一定比当前数小),那么当 root [k] 时主席树上的值就等于之前对k建的线段树,主席树上我们维护三个值,一个是区间和,一个是右端最大,以及左端最大。

    实现代码:

    #include<bits/stdc++.h>
    using namespace std;
    #define mid int m = (l + r) >> 1
    const int M = 2e4 + 10;
    int sum[M*20],lsum[M*20],rsum[M*20],ls[M*20],rs[M*20],root[M],b[6];
    int idx,n;
    struct node{
        int val,id;
    }a[M];
    
    bool cmp(node x,node y){
        return x.val < y.val;
    }
    
    void pushup(int rt){
        sum[rt] = sum[ls[rt]] + sum[rs[rt]];
        lsum[rt] = max(lsum[ls[rt]],sum[ls[rt]]+lsum[rs[rt]]);
        rsum[rt] = max(rsum[rs[rt]],sum[rs[rt]]+rsum[ls[rt]]);
    }
    
    void build(int l,int r,int &rt){
        rt = ++idx;
        if(l == r){
            sum[rt] = lsum[rt] = rsum[rt] = 1;
            return ;
        }
        mid;
        build(l,m,ls[rt]); build(m+1,r,rs[rt]);
        pushup(rt);
    }
    
    void update(int p,int l,int r,int old,int &rt){
        rt = ++idx; ls[rt] = ls[old]; rs[rt] = rs[old];
        if(l == r){
            sum[rt] = lsum[rt] = rsum[rt] = -1;
            return ;
        }
        mid;
        if(p <= m) update(p,l,m,ls[old],ls[rt]);
        else update(p,m+1,r,rs[old],rs[rt]);
        pushup(rt);
    }
    
    int query_sum(int L,int R,int l,int r,int rt){
        if(L <= l&&R >= r){
            return sum[rt];
        }
        mid;
        int ret = 0;
        if(L <= m) ret += query_sum(L,R,l,m,ls[rt]);
        if(R > m) ret += query_sum(L,R,m+1,r,rs[rt]);
        return ret;
    }
    
    int query_lsum(int L,int R,int l,int r,int rt){
        if(L <= l&&R >= r){
            return lsum[rt];
        }
        mid;
        if(R <= m) return query_lsum(L,R,l,m,ls[rt]);
        else if(L > m) return query_lsum(L,R,m+1,r,rs[rt]);
        else return max(query_lsum(L,R,l,m,ls[rt]),query_sum(L,R,l,m,ls[rt])+query_lsum(L,R,m+1,r,rs[rt]));
    }
    
    int query_rsum(int L,int R,int l,int r,int rt){
        if(L <= l&&R >= r){
            return rsum[rt];
        }
        mid;
        if(R <= m) return query_rsum(L,R,l,m,ls[rt]);
        else if(L > m) return query_rsum(L,R,m+1,r,rs[rt]);
        else return max(query_rsum(L,R,m+1,r,rs[rt]),query_sum(L,R,m+1,r,rs[rt])+query_rsum(L,R,l,m,ls[rt]));
    }
    
    bool check(int a,int b,int c,int d,int rt){
        int cnt = 0;
        if(c-1 > b) cnt += query_sum(b+1,c-1,1,n,root[rt]);
        cnt += query_rsum(a,b,1,n,root[rt]);
        cnt += query_lsum(c,d,1,n,root[rt]);
        return cnt >= 0;
    }
    
    int main()
    {
        scanf("%d",&n);
        for(int i = 1;i <= n;i ++){
            scanf("%d",&a[i].val);
            a[i].id = i;
        }
        sort(a+1,a+1+n,cmp);
        build(1,n,root[1]);
        for(int i = 2;i <= n;i ++)
            update(a[i-1].id,1,n,root[i-1],root[i]);
        int q,last = 0;
        scanf("%d",&q);
        for(int i = 1;i <= q;i ++){
            scanf("%d%d%d%d",&b[1],&b[2],&b[3],&b[4]);
            for(int j = 1;j <= 4;j ++)
                b[j] = (b[j]+last)%n;
            sort(b+1,b+5);
            b[1]++; b[2]++; b[3]++; b[4]++;
            int l = 1,r = n,k;
            while(l <= r){
                mid;
                if(check(b[1],b[2],b[3],b[4],m))
                    k = m,l = m+1;
                else r = m - 1;
            }
            last = a[k].val;
            printf("%d
    ",last);
        }
    }
  • 相关阅读:
    ASP.NET在禁用视图状态的情况下仍然使用ViewState对象【转】
    Atcoder Regular Contest 061 D Card Game for Three(组合数学)
    Solution 「CERC 2016」「洛谷 P3684」机棚障碍
    Solution 「CF 599E」Sandy and Nuts
    Solution 「洛谷 P6021」洪水
    Solution 「ARC 058C」「AT 1975」Iroha and Haiku
    Solution 「POI 2011」「洛谷 P3527」METMeteors
    Solution 「CF 1023F」Mobile Phone Network
    Solution 「SP 6779」GSS7
    Solution 「LOCAL」大括号树
  • 原文地址:https://www.cnblogs.com/kls123/p/10041366.html
Copyright © 2011-2022 走看看