2019牛客暑假多校训练赛第七场 E Find the median(权值线段树)

题目链接:https://ac.nowcoder.com/acm/contest/887/E

 

题意:给出一个空集合,每次给出一个l,r的区间将l,r的这些数全部扔进区间内,每次要求输出此时的中位数是多少,若是集合是偶数个数则输出小的那个中位数。

数据范围:1<=N,l,r<=4e5

 

思路:首先离散化一下,然后进行类似上一篇博客那样建树,及线段树的每个节点都是一个左闭右开的区间,那么对于每次插入数只需要找到对应的区间将数插入即可,此题的lazy标记只需要一直储存区间的和即可。每次在线段树上找中间大的数就好了。难点还是在于那个左闭右开不重不漏的建树。

#include<bits/stdc++.h>
#define ls rt<<1
#define rs rt<<1|1
using namespace std;
typedef long long ll;
const int N=4e5+5;
int n,m,t,cnt;
ll A1,A2,B1,B2,C1,C2,M1,M2,b[N<<1],x[N],y[N],sz[N<<3],lazy[N<<3];
void up(int rt,int l,int r,ll val){
    sz[rt]+=(b[r+1]-b[l])*val;
    lazy[rt]+=val;
}
void update(int rt,int l,int r,int ql,int qr){
    if(ql<=l&&r<=qr){
        up(rt,l,r,1);
        return ;
    }
    int mid=(l+r)>>1;
    if(lazy[rt]){
        up(ls,l,mid,lazy[rt]);
        up(rs,mid+1,r,lazy[rt]);
        lazy[rt]=0;
    }
    if(ql<=mid) update(ls,l,mid,ql,qr);
    if(qr>mid) update(rs,mid+1,r,ql,qr);
    sz[rt]=sz[ls]+sz[rs];
}
ll query(int rt,int l,int r,ll pos){
    if(l==r){
        int ti=sz[rt]/(b[l+1]-b[l]);
        return b[l]+(pos-1)/ti;
        ///return b[l]-1+ceil(1.0*pos/ti);
    }
    int mid=(l+r)/2;
    if(lazy[rt]){
        up(ls,l,mid,lazy[rt]);
        up(rs,mid+1,r,lazy[rt]);
        lazy[rt]=0;
    }
    if(sz[ls]>=pos) return query(ls,l,mid,pos);
    else return query(rs,mid+1,r,pos-sz[ls]);
}
int main(){
    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);
    for(int i=3;i<=n;i++){
        x[i]=(A1*x[i-1]+B1*x[i-2]+C1)%M1;
        y[i]=(A2*y[i-1]+B2*y[i-2]+C2)%M2;
    }
    for(int i=1;i<=n;i++){
        if(x[i]>y[i])swap(x[i],y[i]);
        x[i]++,y[i]++;
        b[++cnt]=x[i],b[++cnt]=y[i]+1;
    }
    sort(b+1,b+cnt+1);
    cnt=unique(b+1,b+cnt+1)-b-1;
    ll sum=0;
    for(int i=1;i<=n;i++){
        sum+=(y[i]-x[i]+1);
        x[i]=lower_bound(b+1,b+cnt+1,x[i])-b;
        y[i]=lower_bound(b+1,b+cnt+1,y[i]+1)-b;
        update(1,1,cnt-1,x[i],y[i]-1);
        printf("%lld\n",query(1,1,cnt-1,(sum+1)/2));
    }
    return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值