2019牛客暑期多校训练营(第七场)- Find the median

线段树

显然的方法是把区间离散化以后,维护每个区间内出现的数的个数。

查询的时候直接用左端点的值加上查询的数的排名/区间插入的次数就行了。

处理这类问题一般是让每个端点的右区间+1,然后用叶子节点来保存一段区间的信息。

当我们的线段树递归到叶子结点的时候,l = r, 我们如果让线段树维护当前区间每个数出现的次数,叶子节点显然不好处理,因为l = r。

所以我们可以让每个区间维护的信息往后扩展一点。

假设当前区间为[l...r], 这里l和r均为离散化以后的值。此时可以让线段树维护[l..r+1)区间内每个数出现的次数,这样到了叶子节点实际上是[l, l+1)区间内每个数出现的次数,(此时l=r)。

这样问题就简化完成了。

在修改的时候也要让右端点-1,这样才符合我们对维护的信息的定义.

#include <bits/stdc++.h>
#define INF 0x3f3f3f3f
#define full(a, b) memset(a, b, sizeof a)
#define __fastIn ios::sync_with_stdio(false), cin.tie(0)
#define range(x) (x).begin(), (x).end()
#define pb push_back
using namespace std;
typedef long long LL;
inline int lowbit(int x){ return x & (-x); }
inline int read(){
    int ret = 0, w = 0; char ch = 0;
    while(!isdigit(ch)){
        w |= ch == '-', ch = getchar();
    }
    while(isdigit(ch)){
        ret = (ret << 3) + (ret << 1) + (ch ^ 48);
        ch = getchar();
    }
    return w ? -ret : ret;
}
template <typename A>
inline A __lcm(A a, A b){ return a / __gcd(a, b) * b; }
template <typename A, typename B, typename C>
inline A fpow(A x, B p, C lyd){
    A ans = 1;
    for(; p; p >>= 1, x = 1LL * x * x % lyd)if(p & 1)ans = 1LL * x * ans % lyd;
    return ans;
}
const int N = 400005;
int n, x[N], y[N], L[N], R[N], a1, b1, c1, a2, b2, c2, m1, m2, b[N<<1], tot;
int lazy[N<<3];
LL freq[N<<3];
 
void add(int rt, int l, int r, int val){
    lazy[rt] += val;
    freq[rt] += 1LL * (b[r + 1] - b[l]) * val;
}
 
void push_down(int rt, int l, int r, int val){
    int mid = (l + r) >> 1;
    add(rt << 1, l, mid, val);
    add(rt << 1 | 1, mid + 1, r, val);
    lazy[rt] = 0;
}
 
void push_up(int rt){
    freq[rt] = freq[rt << 1] + freq[rt << 1 | 1];
}
 
void buildTree(int rt, int l, int r){
    if(l == r){
        freq[rt] = lazy[rt] = 0;
        return;
    }
    int mid = (l + r) >> 1;
    buildTree(rt << 1, l, mid);
    buildTree(rt << 1 | 1, mid + 1, r);
    push_up(rt);
}
 
void insert(int rt, int l, int r, int il, int ir){
    if(l == il && r == ir){
        add(rt, l, r, 1);
        return;
    }
    push_down(rt, l, r, lazy[rt]);
    int mid = (l + r) >> 1;
    if(ir <= mid) insert(rt << 1, l, mid, il, ir);
    else if(il > mid) insert(rt << 1 | 1, mid + 1, r, il, ir);
    else insert(rt << 1, l, mid, il, mid), insert(rt << 1 | 1, mid + 1, r, mid + 1, ir);
    push_up(rt);
}
 
int query(int rt, int l, int r, LL k){
    if(l > r) return 0;
    if(l == r){
        return b[l] + (k - 1) / lazy[rt];
    }
    push_down(rt, l, r, lazy[rt]);
    int mid = (l + r) >> 1;
    if(freq[rt << 1] >= k) return query(rt << 1, l, mid, k);
    return query(rt << 1 | 1, mid + 1, r, k - freq[rt << 1]);
}
 
int main(){
 
    scanf("%d", &n);
    scanf("%d%d%d%d%d%d", &x[1], &x[2], &a1, &b1, &c1, &m1);
    scanf("%d%d%d%d%d%d", &y[1], &y[2], &a2, &b2, &c2, &m2);
    for(int i = 3; i <= n; i ++){
        x[i] = (1LL * a1 * x[i - 1] + 1LL * b1 * x[i - 2] + c1) % m1;
        y[i] = (1LL * a2 * y[i - 1] + 1LL * b2 * y[i - 2] + c2) % m2;
    }
    for(int i = 1; i <= n; i ++){
        L[i] = min(x[i], y[i]) + 1;
        R[i] = max(x[i], y[i]) + 1;
        R[i] ++, b[++tot] = L[i], b[++tot] = R[i];
    }
    sort(b + 1, b + tot + 1);
    tot = (int)(unique(b + 1, b + tot + 1) - b - 1);
//    b[tot + 1] = b[tot] + 1;
//    tot ++;
    LL sum = 0;
    buildTree(1, 1, tot);
    for(int i = 1; i <= n; i ++){
        sum += R[i] - L[i];
        L[i] = (int)(lower_bound(b + 1, b + tot + 1, L[i]) - b);
        R[i] = (int)(lower_bound(b + 1, b + tot + 1, R[i]) - b);
        insert(1, 1, tot, L[i], R[i] - 1);
        printf("%d\n", query(1, 1, tot, (sum + 1) / 2));
    }
    return 0;
}

转载于:https://www.cnblogs.com/onionQAQ/p/11328190.html

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值