2019牛客暑期多校训练营(第七场)E Find the median —— 线段树求大数据中位数

This way

题意:

你一开始有一个空序列,每次在你的序列中塞入l-r中所有的值,并且问你现在你的序列的中位数是什么。

题解:

二分+线段树TLE。。。重算一遍才发现忘记将lower_bound的复杂度算进去了。
需要用两个数组维护:sum[]表示区间内值的个数,flag[]是延时标记,同时它如果延到叶子结点了是不会清空的,那么在这里就可以表示这个位置出现了多少次来用。我们将所有输入离散化,这时候就有一个问题:我怎么知道我要查询的位置出现了几次?它可以看做是和它左边第一个在输入中出现过的数字是相等的。为什么?因为如果这个数不可能是中位数,那么我们就不会查到他,如果它可能是,那么我们线段树是按照从小到大建树的,那么肯定会查到它或者它左边的值。
对于为什么rig要+2,因为在线段树中,如果直接维护离散化区间的值,就比如说原数组第三个位置是3,第4个位置是5,第5个位置是6,那么我们如果加入3-6,对于离散化的结果是不会变的,但是线段树里面就少了一个4的内容。但是如果直接维护a[r+1]-a[l]+1的话,那么push_down时会多加一次,所以我们直接用r+2,这样不会错

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define pa pair<ll,ll>
const int N=4e5+5;
ll x[N],y[N],a1,b1,c1,m1,a2,b2,c2,m2;
ll a[N*2],flag[N*2*4],sum[N*2*4],num[N*2*4];
void push_down(int l,int r,int root)
{
    if(!flag[root])
        return ;
    int mid=l+r>>1;
    sum[root<<1]+=(ll)(a[mid+1]-a[l])*flag[root];
    sum[root<<1|1]+=(ll)(a[r+1]-a[mid+1])*flag[root];
    flag[root<<1]+=flag[root];
    flag[root<<1|1]+=flag[root];
    flag[root]=0;
}
void update(int l,int r,int root,int ql,int qr)
{
    if(l>=ql&&r<=qr)
    {
        sum[root]+=(ll)a[r+1]-a[l];
        flag[root]++;
        return ;
    }
    push_down(l,r,root);
    int mid=l+r>>1;
    if(mid>=ql)
        update(l,mid,root<<1,ql,qr);
    if(mid<qr)
        update(mid+1,r,root<<1|1,ql,qr);
    sum[root]=sum[root<<1]+sum[root<<1|1];
}
ll query(int l,int r,int root,ll pos)
{
    if(l==r)
    {
        //printf("**:a[l]:%d %d\n",a[l],a[l]+(pos-1)/num[root]);
        return a[l]+(pos-1)/flag[root];
    }
    int mid=l+r>>1;
    push_down(l,r,root);
    if(sum[root<<1]<pos)
        return query(mid+1,r,root<<1|1,pos-sum[root<<1]);
    else
        return query(l,mid,root<<1,pos);
}
int all,le[N],rig[N];
ll all_num;
int main()
{
    int n;
    scanf("%d",&n);
    scanf("%lld%lld%lld%lld%lld%lld",&x[1],&x[2],&a1,&b1,&c1,&m1);
    scanf("%lld%lld%lld%lld%lld%lld",&y[1],&y[2],&a2,&b2,&c2,&m2);
    a[1]=x[1]+1,a[2]=x[2]+1,a[3]=y[1]+1,a[4]=y[2]+1;
    for(int i=3;i<=n;i++)
        x[i]=(x[i-1]*a1+x[i-2]*b1+c1)%m1,y[i]=(y[i-1]*a2+y[i-2]*b2+c2)%m2;
    for(int i=1;i<=n;i++)
    {
        le[i]=min(x[i],y[i])+1;
        rig[i]=max(x[i],y[i])+2;
        a[i*2-1]=le[i],a[i*2]=rig[i];
    }
    sort(a+1,a+1+n*2);
    all=unique(a+1,a+1+n*2)-a-1;
    a[all+1]=a[all]+1;
    for(int i=1;i<=n;i++)
        le[i]=lower_bound(a+1,a+1+all,le[i])-a,rig[i]=lower_bound(a+1,a+1+all,rig[i])-a;
    int last=0;
    for(int i=1;i<=n;i++)
    {
        all_num+=(ll)a[rig[i]]-a[le[i]];
        update(1,all,1,le[i],rig[i]-1);
        printf("%d\n",query(1,all,1,(all_num+1)/2));
    }
    return 0;
}
/*
5
3 1 4 1 5 9
2 7 1 8 2 9
*/

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值