【BZOJ 3932】[CQOI2015]任务查询系统

本文介绍了一种使用主席树解决区间更新问题的方法,通过将区间操作转化为点操作,并利用主席树维护权值的出现次数及权值和,实现区间查询的高效处理。特别注意在处理答案时对最后一个节点的状态进行特殊判断。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目来源:BZOJ 3932

思路:

类似于在区间上标记线段的操作,把所有的区间变成两个点,边操作边进行前缀和就好了,需要用用主席树维护的信息有权值的出现次数,权值的和。
特别注意,在处理答案的时候,最后一个节点可能有多种状态,有可能只选择其中的一部分,需要特判一下。

代码:

#include <iostream>
#include <cstdio>
#include <algorithm>
#define mid ((l+r)>>1)
using namespace std;
const int maxn = 100010;
typedef long long ll;
struct node{
    int time, val;
    bool operator < (const node &tt)const{return time < tt.time;}
} p[maxn*2];
struct Tree{
    int num, sum;
    Tree *ch[2];
} T[maxn*40], *root[maxn*2];
int n, m, cnt, nxt, ct, _tm;
int val[maxn], last[maxn], tm[maxn];
ll Pre = 1;
ll gt(){
    char c = ' '; ll num = 0; bool ok = 0;
    while(1){
        c = getchar();
        if(c <= '9' && c >= '0') num = num * 10 + c - '0', ok = 1;
        else if(ok) return num;
    }
}
int find(int ee[], int l, int r, int v){
    int ans = 0;
    while(l <= r){
        if(ee[mid] <= v) ans = mid, l = mid + 1;
        else r = mid - 1;
    }
    return ans;
}
int abs(int x){return x < 0 ? -x : x;}
Tree* get(){return &T[++nxt];}
void made(Tree* now, int l, int r){
    if(l == r) return;
    made(now->ch[0] = get(), l, mid);
    made(now->ch[1] = get(), mid+1, r);
}
void build(Tree* now, Tree* pre, int k, int Num, int Sum, int l, int r){
    if(l == r) return;
    int wh = 1; if(k <= mid) wh = 0;
    now->ch[wh^1] = pre->ch[wh^1];
    now->ch[wh] = get();
    now->ch[wh]->num = pre->ch[wh]->num + Num;
    now->ch[wh]->sum = pre->ch[wh]->sum + Sum;
    build(now->ch[wh], pre->ch[wh], k, Num, Sum, 
    wh == 0 ? l : mid+1, wh == 0 ? mid : r);
}
ll ask(Tree* now, int k, int l, int r){
    if(l == r){
        // 这里特判。
        if(now->num) return now->sum / now->num * k;
        return now->sum;
    }
    if(k <= now->ch[0]->num) return ask(now->ch[0], k, l, mid);
    else return now->ch[0]->sum + ask(now->ch[1], k-now->ch[0]->num, mid+1, r);
}
int main(){
    scanf("%d%d", &m, &n);
    for(int i = 1; i <= m; i ++){
        int a = gt(), b = gt(), c = gt();
        p[++cnt].time = a, p[cnt].val = c;
        p[++cnt].time = b+1, p[cnt].val = -c;
        val[++ct] = c;
    }
    // 按时间排序 
    sort(p+1, p+1+cnt);
    // 离散化 
    sort(val+1, val+1+ct);
    ct = unique(val+1, val+1+ct) - (val+1);
    // 建树 
    root[0] = get(); made(root[0], 1, ct);
    for(int i = 1; i <= cnt; i ++){
        last[p[i].time] = i;
        int pos = find(val, 1, ct, abs(p[i].val));
        root[i] = get();
        build(root[i], root[i-1], pos, p[i].val > 0 ? 1:-1, p[i].val, 1, ct);
    }
    for(int i = 1; i <= n; i ++)
        if(last[i] != 0) tm[++_tm] = i;

    // 查询 
    for(int i = 1; i <= n; i ++){
        ll ti = gt(), a = gt(), b = gt(), c = gt(), ki;
        ti = tm[find(tm, 1, _tm, ti)]; 
        ki = (((a%c)*(Pre%c))%c+(b%c))%c + 1;
        Pre = ask(root[last[ti]], ki, 1, ct);
        printf("%lld\n", Pre);
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值