2019 牛客多校 第七场 E.Find the Median (线段树,(叶子) 点代区间)

戳我看题

题意:有 n 次操作,每一次操作都向集合中插入 [L,R] 之间的所有数。询问每次操作之后的中位数

思路:第一次写叶子节点代替一个区间这样的题。

首先肯定是对所有端点离散化,但是离散化的时候需要将右端点 + 1(这样就表示一个左闭右开的区间)。例:

L = [2, 4]
R = [7, 8]

(右端点 + 1)离散化后有:

数组  Ls:      2  5  7  9

下标  index: 1  2  3  4

叶子代表的就是 每相邻两个数之间的  一个左闭右开的区间。①:[2,4) , ②:[4,7) , ③:[7,8)

 

换句话说,区间(叶子)  代替的是数:2,3   ②:4, 5, 6,  ③:7

利用线段树区间和,就维护了这些区间有多少个数。

查询的时候,当 L == R 时,可一直通过 Ls[L] 直接确定这个区间第一个数,那么显然  sum[i] / (Ls[L + 1] - Ls[L]) 就表示这个区间每种数有多少个。   也就可以随意确定这个区间的第几个数是谁, 加上 Ls[L] 即可确定中位数

Code:

#include<bits/stdc++.h>
#define debug(x) cout << "[" << #x <<": " << (x) <<"]"<< endl
#define pii pair<int,int>
#define clr(a,b) memset((a),b,sizeof(a))
#define rep(i,a,b) for(int i = a;i < b;i ++)
#define pb push_back
#define MP make_pair
#define LL long long
#define ull unsigned LL
#define ls i << 1
#define rs (i << 1) + 1
#define fi first
#define se second
#define CLR(a) while(!(a).empty()) a.pop()

using namespace std;

const int maxn = 4e5 + 10;
LL x[maxn],y[maxn];
LL A1,B1,A2,B2,C1,C2,M1,M2;
int L[maxn],R[maxn];
int Ls[maxn << 1],en;

int getId(int x){
    return lower_bound(Ls + 1,Ls + 1 + en,x) - Ls;
}

LL sum[maxn * 8];
int lazy[maxn * 8];

void pushDown(int i,int l,int r){
    if(lazy[i]){
        int mid = (l + r) >> 1;
        sum[ls] += 1LL * (Ls[mid + 1] - Ls[l]) * lazy[i];
        sum[rs] += 1LL * (Ls[r + 1] - Ls[mid + 1]) * lazy[i];
        lazy[ls] += lazy[i];
        lazy[rs] += lazy[i];
        lazy[i] = 0;
    }
}

void update(int i,int l,int r,int ul,int ur){
    if(ul <= l && r <= ur){
        sum[i] += Ls[r + 1] - Ls[l];
        ++ lazy[i];
        return ;
    }
    pushDown(i,l,r);
    int mid = (l + r) >> 1;
    if(ul <= mid) update(ls,l,mid,ul,ur);
    if(ur > mid) update(rs,mid + 1,r,ul,ur);
    sum[i] = sum[ls] + sum[rs];
}

LL query(int i,int l,int r,LL k){
    if(l == r){
        LL Every = sum[i] / (Ls[r + 1] - Ls[l]);
        return (LL) Ls[l] + (k - 1) / Every;
    }
    pushDown(i,l,r);
    int mid = (l + r) >> 1;
    if(sum[ls] >= k) return query(ls,l,mid,k);
    return query(rs,mid + 1,r,k - sum[ls]);
}

int main() {
#ifndef ONLINE_JUDGE
    freopen("in.txt", "r", stdin);
    freopen("out.txt", "w", stdout);
#endif
    int n; scanf("%d",&n);
    cin >> x[1] >> x[2] >> A1 >> B1 >> C1 >> M1;
    cin >> 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){
        L[i] = (int) min(x[i],y[i]) + 1;
        R[i] = (int) max(x[i],y[i]) + 1;
        Ls[++ en] = L[i]; Ls[++ en] = R[i] + 1;
    }
    sort(Ls + 1,Ls + en + 1);
    en = unique(Ls + 1,Ls + en + 1) - Ls - 1;
    //for(int i = 1;i <= en;++ i) printf("%2d ",i); printf("\n");
    //for(int i = 1;i <= en;++ i) printf("%2d ",Ls[i]); printf("\n");
    LL xx = 0;
    for(int i = 1;i <= n;++ i){
        xx += (R[i] - L[i] + 1);
        update(1,1,en,getId(L[i]),getId(R[i] + 1) - 1);
        printf("%lld\n",query(1,1,en,(xx + 1) / 2));
    }
    return 0;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值