zoukankan      html  css  js  c++  java
  • BZOJ2653middle——二分答案+可持久化线段树

    题目描述

    一个长度为n的序列a,设其排过序之后为b,其中位数定义为b[n/2],其中a,b从0开始标号,除法取下整。给你一个
    长度为n的序列s。回答Q个这样的询问:s的左端点在[a,b]之间,右端点在[c,d]之间的子序列中,最大的中位数。
    其中a<b<c<d。位置也从0开始标号。我会使用一些方式强制你在线。

    输入

    第一行序列长度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

    输出

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

    样例输入

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

    样例输出

    271451044
    271451044
    969056313
     
      对于一个序列,如果序列有奇数个数,那么中位数是中间那个数,如果有偶数个数,那么中位数是中间两个中后面那个。如果要判断M是否为合法的中位数,就把序列中>=M的数权值设为1,<M的数权值设为-1,只要有一段区间的最大连续子段和>=0,那么M就是合法的。因此求一段区间的中位数可以二分中位数是什么,只要rmax(a,b-1)+sum(b,c)+lmax(c+1,d)大于零那么这个数就可能成为中位数(其中sum表示区间和,lmax表示区间从左端点开始最大连续子段和,rmax表示区间从右端点开始最大连续子段和)。对于每次二分答案要将线段树中小于答案的数权值设为-1,其他数权值设为1,求上述最大连续子段和。但如果每次二分答案都重新建树显然太慢了,因此可以用主席树,每个时刻i的主席树表示以第i个数为中位数时线段树的状态,第一个时刻将所有位置置为1,然后下一时刻将最小的数那个位置权值置为-1,以此类推。每次二分答案只要查询对应时刻的主席树就好了。
    #include<set>
    #include<map>
    #include<queue>
    #include<cmath>
    #include<stack>
    #include<vector>
    #include<cstdio>
    #include<cstring>
    #include<iostream>
    #include<algorithm>
    typedef long long ll;
    using namespace std;
    int n,m;
    int sum[5000010];
    int lmx[5000010];
    int rmx[5000010];
    int ls[5000010];
    int rs[5000010];
    int root[50010];
    int a,b,c,d;
    int cnt;
    int ans;
    int p[5];
    struct node
    {
        int num;
        int id;
    }s[20010];
    bool cmp(node a,node b)
    {
        return a.num<b.num;
    }
    void pushup(int rt)
    {
        sum[rt]=sum[ls[rt]]+sum[rs[rt]];
        lmx[rt]=max(lmx[ls[rt]],sum[ls[rt]]+lmx[rs[rt]]);
        rmx[rt]=max(rmx[rs[rt]],sum[rs[rt]]+rmx[ls[rt]]);
    }
    int build(int l,int r)
    {
        int rt=++cnt;
        if(l==r)
        {
            sum[rt]=1;
            lmx[rt]=1;
            rmx[rt]=1;
            return rt;
        }
        int mid=(l+r)>>1;
        ls[rt]=build(l,mid);
        rs[rt]=build(mid+1,r);
        pushup(rt);
        return rt;
    }
    int updata(int pre,int l,int r,int k)
    {
        int rt=++cnt;
        if(l==r)
        {
            sum[rt]=-1;
            lmx[rt]=0;
            rmx[rt]=0;
            return rt;
        }
        ls[rt]=ls[pre];
        rs[rt]=rs[pre];
        int mid=(l+r)>>1;
        if(k<=mid)
        {
            ls[rt]=updata(ls[pre],l,mid,k);
        }
        else
        {
            rs[rt]=updata(rs[pre],mid+1,r,k);
        }
        pushup(rt);
        return rt;
    }
    int query(int rt,int l,int r,int L,int R)
    {
        if(L>R)
        {
            return 0;
        }
        if(L<=l&&r<=R)
        {
            return sum[rt];
        }
        int mid=(l+r)>>1;
        if(L>mid)
        {
            return query(rs[rt],mid+1,r,L,R);
        }
        else if(R<=mid)
        {
            return query(ls[rt],l,mid,L,R);
        }
        return query(ls[rt],l,mid,L,R)+query(rs[rt],mid+1,r,L,R);
    }
    int findl(int rt,int l,int r,int L,int R)
    {
        if(L>R)
        {
            return 0;
        }
        if(L<=l&&r<=R)
        {
            return rmx[rt];
        }
        int mid=(l+r)>>1;
        int res=0;
        if(R>mid)
        {
            res=findl(rs[rt],mid+1,r,L,R);
        }
        if(L<=mid)
        {
            res=max(res,findl(ls[rt],l,mid,L,R)+query(rs[rt],mid+1,r,mid+1,R));
        }
        return res;
    }
    int findr(int rt,int l,int r,int L,int R)
    {
        if(L>R)
        {
            return 0;
        }
        if(L<=l&&r<=R)
        {
            return lmx[rt];
        }
        int mid=(l+r)>>1;
        int res=0;
        if(L<=mid)
        {
            res=findr(ls[rt],l,mid,L,R);
        }
        if(R>mid)
        {
            res=max(res,findr(rs[rt],mid+1,r,L,R)+query(ls[rt],l,mid,L,mid));
        }
        return res;
    }
    bool check(int x,int a,int b,int c,int d)
    {
        int res=0;
        res+=query(root[x],1,n,b,c);
        res+=findl(root[x],1,n,a,b-1);
        res+=findr(root[x],1,n,c+1,d);
        if(res>=0)
        {
            return true;
        }
        return false;
    }
    int main()
    {
        scanf("%d",&n);
        for(int i=1;i<=n;i++)
        {
            scanf("%d",&s[i].num);
            s[i].id=i;
        }
        root[1]=build(1,n);
        sort(s+1,s+1+n,cmp);
        for(int i=2;i<=n;i++)
        {
            root[i]=root[i-1];
            root[i]=updata(root[i],1,n,s[i-1].id);
        }
        scanf("%d",&m);
        for(int i=1;i<=m;i++)
        {
            scanf("%d%d%d%d",&a,&b,&c,&d);
            p[1]=(a+ans)%n;
            p[2]=(b+ans)%n;
            p[3]=(c+ans)%n;
            p[4]=(d+ans)%n;
            sort(p+1,p+5);
            a=p[1]+1;
            b=p[2]+1;
            c=p[3]+1;
            d=p[4]+1;
            int l=1;
            int r=n;
            ans=0;
            while(l<=r)
            {
                int mid=(l+r)>>1;
                if(check(mid,a,b,c,d))
                {
                    l=mid+1;
                    ans=mid;
                }
                else
                {
                    r=mid-1;
                }
            }
            ans=s[ans].num;
            printf("%d
    ",ans);
        }
    }
  • 相关阅读:
    vs2012下如何调试带输入参数的程序
    BASH-数据流重导向
    VS在连接期间的一个错误的处理:转换到 COFF 期间失败: 文件无效或损坏
    vmware中NAT配置不能上网的一个解决方案
    linux下查找
    系统及用户的bash环境配置 学习笔记
    linux中控制台字体和背景颜色配置
    bash中变量的巧用
    vi 常用指令存档
    vim指令示意图
  • 原文地址:https://www.cnblogs.com/Khada-Jhin/p/9493055.html
Copyright © 2011-2022 走看看