2019牛客暑期多校训练营(第七场)E Find the median 线段树 特殊存法

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

 

题意:

      给你n个区间,每次往你现有的数中加上l,l+1,.....,r-1,r这些数,问你每次加完这些数后你现在的数中的中位数是多少。

做法:

      先将给你的数进行离散化,然后进行一番特殊的处理后就可以使用线段树了。什么特殊的处理呢,就是把右端点++,然后每次更新的时候只更新离散化后的 [x_{i},y_{i}-1] ,令每次结点更新的数都是(ve[r+1]-ve[l]+1)*倍数。laz延迟更新,保存的是每次加的次数即可。sz[rt]保存的是当前区间里有多少个数,然后我们在查询的过程中,如果左区间有的数sz[lson]大于我们要查的数的个数va,那么我们就在左区间里查,否则我们就在右区间里查第va-sz[lson]小的数就可以了。

       可能有的人和我刚看到这个做法的时候一样的惊奇,老实说我不是第一次碰到这样的题了(第二次的说),所以我还是稍微写点东西记录一下这个做法,下次碰到的时候也好理解,不会理论分析,希望各位感性理解(是我感性理解得出的结果)。

       我们假设要增加的区间是[3,8]和[5,9],第一件事要做的是扩充右区间,如下图,我们把数字3存放在3和4之间的那个位置,所以我们需要扩充右区间来保证数字空间的正确性,第二件事是,我们把所有的数保存在小于等于这个数的第一个数的位置上,即数字8存在数字5的位置上(因为右区间的扩充,其实就已经没有数字8了)。那么我们在加区间[3,8]的时候,其实是在(0,0)这个位置上加了2,在(1,1)这个位置上加了4,即把 3 4 加到了3的位置上,5 6 7 8加到了5的位置上,那么我们在查第3小的数的时候会跑到5这里差第一小的数,然后5+(1-1)=5就是答案了。

        我们可以知道的是,同一个区间里面的每个数的个数都是一样的,比如我又加上了[5,9],那么在5这个和下一个数9差了4的位置上会有8个数,5 5 6 6 7 7 8 8,即我们要得到这里有多少数后除掉就好了。比如要查这里第4小的数,那么我们就是5+(4-1)/2等于6,因为每个数都有两个嘛。

       自认为已经讲的算是清楚了吧,不懂的话再戳就好了(我尽力回答咳咳)。

       


#include<bits/stdc++.h>
#define lson rt<<1
#define rson rt<<1|1
#define rep(i,a,b) for(int i=(int)a;i<=(int)b;i++)
using namespace std;
typedef pair<int,int> pii;
typedef long long ll;
const int maxn=400005;
ll x[maxn],y[maxn],A1,A2,B1,B2,C1,C2,M1,M2;
ll sz[maxn*6],laz[maxn*6];
vector<ll> ve;
void deal(int rt,int l,int r,ll f){
    sz[rt]+=(ve[r+1]-ve[l])*f; laz[rt]+=f;
}

void update(int l,int r,int rt,int ql,int qr){
    if(ql<=l&&r<=qr){
        deal(rt,l,r,1);
        return ;
    }
    int mid=(r+l)/2;
    if(laz[rt]){
        deal(lson,l,mid,laz[rt]);
        deal(rson,mid+1,r,laz[rt]);
        laz[rt]=0;
    }
    if(ql<=mid) update(l,mid,lson,ql,qr);
    if(qr>mid) update(mid+1,r,rson,ql,qr);
    sz[rt]=sz[lson]+sz[rson];
}
ll query(int l,int r,int rt,ll va){
    if(l==r){
        int ti=sz[rt]/(ve[l+1]-ve[l]);
        return ve[l]+(va-1)/ti;
    }
    int mid=(r+l)/2;
    if(laz[rt]){
        deal(lson,l,mid,laz[rt]);
        deal(rson,mid+1,r,laz[rt]);
        laz[rt]=0;
    }
    if(sz[lson]>=va) return query(l,mid,lson,va);
    else return query(mid+1,r,rson,va-sz[lson]);
}
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);
    rep(i,3,n){
        x[i]=(A1*x[i-1]+B1*x[i-2]+C1)%M1;
        y[i]=(A2*y[i-1]+B2*y[i-2]+C2)%M2;
    }
    rep(i,1,n){
        x[i]++,y[i]++;
        if(x[i]>y[i]) swap(x[i],y[i]);
        ve.push_back(x[i]); ve.push_back(y[i]+1);
    }
    sort(ve.begin(),ve.end());
    ve.erase(unique(ve.begin(),ve.end()),ve.end());
    ll sum=0;
    int cnt=ve.size();
    /*for(int i=0;i<cnt;i++){
        printf("ve[%d] = %d\n",i,ve[i]);
    }*/
    rep(i,1,n){
        sum+=(y[i]-x[i]+1);
        x[i]=lower_bound(ve.begin(),ve.end(),x[i])-ve.begin();
        y[i]=lower_bound(ve.begin(),ve.end(),y[i]+1)-ve.begin();
        update(0,cnt-1,1,x[i],y[i]-1);
        printf("%lld\n",query(0,cnt-1,1,(sum+1)/2));
    }
    return 0;
}
/*
2
3 5 1 1 1 1
9 12 1 1 1 1
*/

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值