• bzoj2653 middle


    2653: middle

    Time Limit: 20 Sec  Memory Limit: 512 MB
    Submit: 2091  Solved: 1152
    [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]。  
    输入保证满足条件。
    第一行所谓“排过序”指的是从大到小排序!
     

    Output

    Q行依次给出询问的答案。

    Sample

    5
    170337785
    271451044
    22430280
    969056313
    206452321
    3
    3 1 0 2
    2 3 1 4
    3 1 4 0

    271451044
    271451044
    969056313
    Hint:

      0:n,Q<=100

    1,...,5:n<=2000

    0,...,19:n<=20000,Q<=25000

    分析:挺难有思路的一道题.

              要求最大的中位数,我的第一想法是枚举找出所有可能的中位数,但这明显不行嘛.

       既然不能直接找,那能不能假设我找到了这个最大的中位数,然后判断它是否可行呢?

       emm,似乎可以,但是要怎么判断啊?利用中位数的定义,设目前枚举的中位数为x,≥x的数标记为1,<x的数标记为-1,如果一段区间里的数的和加起来≥0,则证明x可以是中位数.

       why?其实很容易想到如果和>=0,那么x肯定不会小于中位数,但有可能大于中位数,如果大于中位数,接着枚举x+1就可以了.枚举到的最大的满足条件的x就是最大的中位数.显然这个枚举可以替换成二分查找.

       现在已经知道了如何求一段固定的区间的最大的中位数,但是题目中的区间并不是固定的,难道我们要去枚举区间吗?

       一个很简单的转化:最大连续子段和:[b,c]的和+[a,b - 1]的最大右子段和+[c + 1,d]的最大左子段和就是要求的.而求最大子段和可以利用线段树来维护.

       还有一个问题就是:我二分的x是会变的,每次都要重新标记一遍吗?

       别忘了,还有一个利器:主席树.只不过这个主席树和我平时写的主席树都不太一样.这是个利用权值大小来插入,维护“下标”线段树的权值线段树.因为最后的查询给定的是下标.如果用权值线段树,点的分布都不是连续的.非常巧妙,和我以前做过的一道splay题的思想差不多:传送门.

       最后是一些坑点:下标从0开始,不需要去重,左右最大子段和的端点b,c要变成b-1,c+1来查询,查询操作的写法要注意.

       总之,我认为这是一道比较神的题,颠覆了我对主席树的认识与理解.学到了中位数的判定方法和单调性,还学了一种奇怪的主席树的写法.

    #include <cstdio>
    #include <cstring>
    #include <iostream>
    #include <algorithm>
    
    using namespace std;
    
    const int maxn = 20010;
    
    int n,a[maxn],tot,root[maxn],b[maxn],id[maxn];
    int sum[maxn * 20],lsum[maxn * 20],rsum[maxn * 20];
    int lson[maxn * 20],rson[maxn * 20],q,lastans,qq[5];
    int aa,bb,cc,dd;
    
    void pushup(int o)
    {
        int ls = lson[o],rs = rson[o];
        sum[o] = sum[ls] + sum[rs];
        rsum[o] = max(rsum[rs],sum[rs] + rsum[ls]);
        lsum[o] = max(lsum[ls],sum[ls] + lsum[rs]);
    }
    
    void build(int &o,int l,int r)
    {
        o = ++tot;
        if (l == r)
        {
            sum[o] = lsum[o] = rsum[o] = 1;
            return;
        }
        int mid = (l + r) >> 1;
        build(lson[o],l,mid);
        build(rson[o],mid + 1,r);
        pushup(o);
    }
    
    void insert(int l,int r,int x,int &y,int pos,int v)
    {
        y = ++tot;
        if (l == r)
        {
            sum[y] = lsum[y] = rsum[y] = v;
            return;
        }
        lson[y] = lson[x];
        rson[y] = rson[x];
        int mid = (l + r) >> 1;
        if (pos <= mid)
            insert(l,mid,lson[x],lson[y],pos,v);
        else
            insert(mid + 1,r,rson[x],rson[y],pos,v);
        pushup(y);
    }
    
    int query2(int l,int r,int x,int y,int o)
    {
        if (x > y)
            return 0;
        if (x <= l && r <= y)
            return sum[o];
        int mid = (l + r) >> 1,res = 0;
        if (x <= mid)
            res += query2(l,mid,x,y,lson[o]);
        if (y > mid)
            res += query2(mid + 1,r,x,y,rson[o]);
        return res;
    }
    
    int query1(int l,int r,int x,int y,int o)
    {
        if (x > y)
            return 0;
        if (l == x && y == r)
            return rsum[o];
        int mid = (l + r) >> 1;
        if (y <= mid)
            return query1(l,mid,x,y,lson[o]);
        else
            if (x > mid)
                return query1(mid + 1,r,x,y,rson[o]);
        else
            return max(query1(mid + 1,r,mid + 1,y,rson[o]),query2(mid + 1,r,mid + 1,y,rson[o]) + query1(l,mid,x,mid,lson[o]));
    }
    
    int query3(int l,int r,int x,int y,int o)
    {
        if (x > y)
            return 0;
        if (l == x && y == r)
            return lsum[o];
        int mid = (l + r) >> 1;
        if (y <= mid)
            return query3(l,mid,x,y,lson[o]);
        else
            if (x > mid)
                return query3(mid + 1,r,x,y,rson[o]);
        else
            return max(query3(l,mid,x,mid,lson[o]),query2(l,mid,x,mid,lson[o]) + query3(mid + 1,r,mid + 1,y,rson[o]));
    }
    
    bool check(int x)
    {
        return (max(query1(1,n,aa,bb - 1,root[x]),0) + query2(1,n,bb,cc,root[x]) + max(query3(1,n,cc + 1,dd,root[x]),0)) >= 0;
    }
    
    bool cmp(int x,int y)
    {
        return a[x] < a[y];
    }
    
    int main()
    {
        scanf("%d",&n);
        for (int i = 1; i <= n; i++)
        {
            scanf("%d",&a[i]);
            id[i] = i;
        }
        memcpy(b,a,sizeof(a));
        stable_sort(id + 1,id + n + 1,cmp);
        sort(b + 1,b + 1 + n);
        build(root[1],1,n);
        for (int i = 2; i <= n; i++)
            insert(1,n,root[i - 1],root[i],id[i - 1],-1);
        scanf("%d",&q);
        while (q--)
        {
            scanf("%d%d%d%d",&aa,&bb,&cc,&dd);
            qq[1] = (aa + lastans) % n + 1;
            qq[2] = (bb + lastans) % n + 1;
            qq[3] = (cc + lastans) % n + 1;
            qq[4] = (dd + lastans) % n + 1;
            sort(qq + 1,qq + 5);
            aa = qq[1],bb = qq[2],cc = qq[3],dd = qq[4];
            int ans = 0,l = 1,r = n;
            while (l <= r)
            {
                int mid = (l + r) >> 1;
                if (check(mid))
                {
                    ans = mid;
                    l = mid + 1;
                }
                else
                    r = mid - 1;
            }
            lastans = b[ans];
            printf("%d
    ",lastans);
        }
    
        return 0;
    }
  • 相关阅读:
    C# 压缩组件介绍与入门
    《Effective C#》快速笔记(六)-
    原生JS实现简单富文本编辑器
    oracle 给表字段把VARCHAR2 换成 CLOB
    java.lang.NoClassDefFoundError: javax/servlet/ServletOutputStream
    Idea中Module is not specified解决办法
    JS用正则替换特殊字符
    对List<Map>里的map的某个属性重复的值进行处理的方法
    ……
    使用原生js 实现点击消失效果
  • 原文地址:https://www.cnblogs.com/zbtrs/p/8439311.html
Copyright © 2020-2023  润新知