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

    return 0;
}

 

posted @ 2018-02-10 17:58 zbtrs 阅读(...) 评论(...) 编辑 收藏