BZOJ 3932 - 主席树

传送门

mICmI95.png

题目分析

在只打会主席树模板的情况下做了这道题,也算是深有体会。
首先任务可以差分:一个任务是(s, e, p), 则在s处+1, 在e+1处-1,符合前缀。但是我们要查询指定时间的前k任务之和,可以想到主席树,且符合差分性质。
1~n每个节点代表从开始时间到现在,每个节点下是一颗权值线段树。用建立主席树的方法先将新时间节点置为前一个时间(从前一个时间更新),再将当前时间的所有差分操作更新(从当前时间更新),得到的每个时间节点便是从开始到此时间的一颗权值线段树。刚开始本来打的指针,调试被逼无奈改为数组(真·好写)。
建好树过后,对于查询(x, k), 就在x时间节点的这颗权值线段树上查询前k个元素之和,线段树的查询就不赘述了,详见代码。
另外,空间真的是个迷,卡了半天RE。

code

tHwwXRx.png

#include<bits/stdc++.h>
using namespace std;

const int N = 1000005;
typedef long long ll;
int n, c[N * 4], lenc, m;
int p[N], taskCnt, maxx = -1, rt[N];
struct TASK{
    int point, tag, id;
    inline bool operator < (const TASK &b) const{
        return point < b.point;
    }
}task[N];
struct node{
    int lc, rc;
    ll sum, cnt;
    node(){}
}tr[N * 15];
int pool;
vector<int> lists[N];

inline int read(){
    int i = 0, f = 1; char ch = getchar();
    for(; (ch < '0' || ch > '9') && ch != '-'; ch = getchar());
    if(ch == '-') f = -1, ch = getchar();
    for(; ch >= '0' && ch <= '9'; ch = getchar()) i = (i << 3) + (i << 1) + (ch - '0');
    return i * f;
}

inline void wr(ll x){
    if(x < 0) putchar('-'), x = -x;
    if(x > 9) wr(x / 10);
    putchar(x % 10 + '0');
}

inline void disc_init(){
    sort(c + 1, c + lenc + 1);
    lenc = unique(c + 1, c + lenc + 1) - (c + 1);
    for(int i = 1; i <= m; i++) p[i] = lower_bound(c + 1, c + lenc + 1, p[i]) - c;
}

inline void insert(int x, int &y, int l, int r, int v, int cnt){
    tr[y = ++pool] = tr[x];
    tr[y].sum += cnt * c[v], tr[y].cnt += cnt;
    if(l == r) return;
    int mid = l + r >> 1;
    if(v <= mid) insert(tr[x].lc, tr[y].lc, l, mid, v, cnt);
    else insert(tr[x].rc, tr[y].rc, mid + 1, r, v, cnt);
}

inline ll query(int p, int l, int r, int k){
    if(tr[p].cnt <= k) return tr[p].sum;
    if(l == r) return c[l];
    int mid = l + r >> 1;
    if(k <= tr[tr[p].lc].cnt) return query(tr[p].lc, l, mid, k);
    else return query(tr[p].rc, mid + 1, r, k - tr[tr[p].lc].cnt) + tr[tr[p].lc].sum;
}

int main(){
    m = read(), n = read();
    for(int i = 1; i <= m; i++){
        task[++taskCnt].point = read(), task[taskCnt].tag = 1, task[taskCnt].id = i, lists[task[taskCnt].point].push_back(taskCnt);
        task[++taskCnt].point = read() + 1, task[taskCnt].tag = -1, task[taskCnt].id = i, lists[task[taskCnt].point].push_back(taskCnt);
        
        p[i] = c[++lenc] = read();
    }
    
    disc_init();
    
    for(int i = 1; i <= n; i++){
        insert(rt[i - 1], rt[i], 1, lenc, 0, 0);
        for(int j = 0; j < lists[i].size(); j++)
            insert(rt[i], rt[i], 1, lenc, p[task[lists[i][j]].id], task[lists[i][j]].tag);
    }

    ll pre = 1, ans;
    for(int i = 1; i <= n; i++){
        int x = read(), a = read(), b = read(), c = read();
        ans = query(rt[x], 1, lenc, ((a * pre + b) % c) + 1);
        wr(ans), putchar('\n');
        pre = ans;
    }
}

转载于:https://www.cnblogs.com/CzYoL/p/7643638.html

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值