zoukankan      html  css  js  c++  java
  • BZOJ2653 middle 【主席树】【二分】*

    BZOJ2653 middle


    Description

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

    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


    这应该算是一道思路题,首先如果对于一个数我们确定了它的值x我们应该怎么做??首先,我们发现所有小于x的数对x是不是中位数的贡献是等价的,同理所有大于x的数对x是不是中位数的贡献是等价的,那么我们可以把所有大于x的数赋值为1,把所有小于x的数赋值为-1,然后我们就可以利用这里的一个性质:当一个区间的最大子段和大于等于0,x一定可以在某种情况下成为中位数,至于为什么。。首先我们发现随着x增大,区间的最大子段和是单调递减的,所以当区间最大子段和大于等于0的时候,当前节点合法,并且显然可能有比它更大的数成为中位数

    然后我们考虑怎么对这个区间最大字段和进行维护,首先b~c的节点是必须要选的,我们就维护sum就可以了,然后对于a到b的区间和c到d的区间我们考虑维护最大右子段和和最大左子段和,这些操作都可以在线段树上进行实现,很简单,就不详细说了

    那么显然我们是不可能二分每个数然后暴力修改成1和-1的,所以我们考虑将权值作为一个维度,随着权值增加我们修改1的值为-1,这个操作我们可以用主席树实现,我们就根据权值大小来建立主席树,每一棵线段树中以在原序列中的位置作为下标进行维护,然后更新一下,就做完了啦


    以前只觉得主席树可以维护一下区间第K大这样板子问题,不知道还可以利用可持久化的性质根据权值的大小来建树,涨姿势了


    #include<bits/stdc++.h>
    using namespace std;
    #define N 20010
    #define M 400010
    int n,m,cnt=0,tot=0,lastans=0;
    int x[N],pre[N],q[5];
    int root[N],ls[M],rs[M],sum[M],lsum[M],rsum[M];
    vector<int> G[N],p;
    map<int,int> mp;
    void pushnow(int rt,int vl){sum[rt]=lsum[rt]=rsum[rt]=vl;}
    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 &rt,int l,int r){
        rt=++cnt;
        if(l==r){pushnow(rt,1);return;}
        int mid=(l+r)>>1;
        build(ls[rt],l,mid);
        build(rs[rt],mid+1,r);
        pushup(rt);
    }
    void modify(int &rt,int last,int l,int r,int pos){
        rt=++cnt;
        if(l==r){pushnow(rt,-1);return;}
        ls[rt]=ls[last];
        rs[rt]=rs[last];
        int mid=(l+r)>>1;
        if(pos<=mid)modify(ls[rt],ls[last],l,mid,pos);
        else modify(rs[rt],rs[last],mid+1,r,pos);
        pushup(rt); 
    }
    int query_sum(int rt,int l,int r,int ql,int qr){
        if(l==ql&&r==qr)return sum[rt];
        int mid=(l+r)>>1;
        if(qr<=mid)return query_sum(ls[rt],l,mid,ql,qr);
        if(ql>mid)return query_sum(rs[rt],mid+1,r,ql,qr);
        return query_sum(ls[rt],l,mid,ql,mid)+query_sum(rs[rt],mid+1,r,mid+1,qr);
    }
    int query_l(int rt,int l,int r,int ql,int qr){
        if(l==ql&&r==qr)return lsum[rt];
        int mid=(l+r)>>1;
        if(qr<=mid)return query_l(ls[rt],l,mid,ql,qr);
        if(ql>mid)return query_l(rs[rt],mid+1,r,ql,qr);
        int ans=query_l(ls[rt],l,mid,ql,mid);
        ans=max(ans,query_sum(ls[rt],l,mid,ql,mid)+query_l(rs[rt],mid+1,r,mid+1,qr));
        return ans;
    }
    int query_r(int rt,int l,int r,int ql,int qr){
        if(l==ql&&r==qr)return rsum[rt];
        int mid=(l+r)>>1;
        if(qr<=mid)return query_r(ls[rt],l,mid,ql,qr);
        if(ql>mid)return query_r(rs[rt],mid+1,r,ql,qr);
        int ans=query_r(rs[rt],mid+1,r,mid+1,qr);
        ans=max(ans,query_sum(rs[rt],mid+1,r,mid+1,qr)+query_r(ls[rt],l,mid,ql,mid));
        return ans;
    }
    bool check(int pos,int a,int b,int c,int d){
        int tmp=0;
        if(c-b>1)tmp+=query_sum(root[pos],1,n,b+1,c-1);
        tmp+=query_l(root[pos],1,n,c,d);
        tmp+=query_r(root[pos],1,n,a,b);
        return tmp>=0;
    }
    int main(){
        scanf("%d",&n);
        for(int i=1;i<=n;i++){
            scanf("%d",&x[i]);
            p.push_back(x[i]);
        }
        sort(p.begin(),p.end());
        for(int i=0;i<p.size();i++)
            if(!mp[p[i]]){
                mp[p[i]]=++tot;
                pre[tot]=p[i]; 
            } 
        for(int i=1;i<=n;i++)G[mp[x[i]]].push_back(i);
        build(root[0],1,n);
        for(int i=1;i<=tot;i++){
            root[i]=root[i-1];
            for(int j=0;j<G[i-1].size();j++)
                modify(root[i],root[i],1,n,G[i-1][j]);
        }
        scanf("%d",&m);
        for(int i=1;i<=m;i++){
            scanf("%d%d%d%d",&q[1],&q[2],&q[3],&q[4]);
            for(int j=1;j<=4;j++)q[j]=(q[j]+lastans)%n+1;
            sort(q+1,q+5);
            int l=1,r=tot,ans=0;
            while(l<=r){
                int mid=(l+r)>>1;
                if(check(mid,q[1],q[2],q[3],q[4]))l=mid+1,ans=mid;
                else r=mid-1;
            }
            printf("%d
    ",lastans=pre[ans]);
        }
        return 0;
    }
  • 相关阅读:
    第一阶段:前端开发_使用JS完成注册页面表单校验完善
    第一阶段:前端开发_使用 JS 完成页面定时弹出广告
    第一阶段:前端开发_使用JS完成首页轮播图效果
    第一阶段:前端开发_使用JS完成注册页面表单校验
    三、Java基础工具(1)_常用类——日期类
    使MySQL支持emoji
    1. Two Sum [Array] [Easy]
    『IOS』 遇到问题记录(长期更新)
    [IOS] 详解图片局部拉伸 + 实现图片局部收缩
    【IOS】模仿"抽屉新热榜"动态启动页YFSplashScreen
  • 原文地址:https://www.cnblogs.com/dream-maker-yk/p/9676345.html
Copyright © 2011-2022 走看看