P3168 [CQOI2015\] 任务查询系统 题解

发布时间 2023-08-22 15:50:47作者: MoyouSayuki

P3168 [CQOI2015] 任务查询系统 题解

因为题目给定的是若干区间,所以考虑差分一下,把区间左端点挂上一个标记,表示到这里的时候多了一个任务,把区间右端点加一挂上一个标记,表示到这里的时候任务消除了。

接着看到第 \(k\) 大,考虑主席树,可以用一排在序列上的主席树维护优先级的前缀和,例如第 \(i\) 棵主席树就维护 \(1\sim i\) 中所有任务差分后的优先级。

然后一次询问就变成了查询一棵主席树的第 \(k\) 大的和,这题就做完了。

时间复杂度:\(O(n\log n + m\log n)\)

注意一些细节,可以先把询问挂在下标上再建立主席树,然后更改时需要删除贡献。

#include <iostream>
#include <cstring>
#include <algorithm>
#include <queue>
#define int long long
using namespace std;
const int N = 2e5 + 10, M = 2e5 + 10, P = 30 * N;
int n, m;
vector<int> q[N];

int disc[N], tot;
int find(int x) {
    return lower_bound(disc + 1, disc + tot + 1, x) - disc;
}
int ls[P], rs[P], dat[P], sum[P], root[N], idx, rt[N], timestamp;
int build(int l, int r) {
    int u = ++ idx;
    if(l == r) return u;
    int mid = l + r >> 1;
    ls[u] = build(l, mid), rs[u] = build(mid + 1, r);
    return u;
}

int update(int q, int l, int r, int x, int v) {
    int p = ++ idx;
    ls[p] = ls[q], rs[p] = rs[q], dat[p] = dat[q], sum[p] = sum[q];
    if(l == r) return dat[p] += v, sum[p] += v * disc[l], p;
    int mid = l + r >> 1;
    if(x <= mid) ls[p] = update(ls[q], l, mid, x, v);
    else rs[p] = update(rs[q], mid + 1, r, x, v);
    dat[p] = dat[ls[p]] + dat[rs[p]];
    sum[p] = sum[ls[p]] + sum[rs[p]];
    return p;
}

int query(int u, int l, int r, int k) { 
    if(l == r) return disc[l] * k;
    int mid = l + r >> 1, t = 0;
    if(dat[ls[u]] >= k) t = query(ls[u], l, mid, k);
    else t = query(rs[u], mid + 1, r, k - dat[ls[u]]) + sum[ls[u]];
    return t;
}
signed main() {
    ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
    cin >> m >> n;
    for(int i = 1, a, b, c; i <= m; i ++) {
        cin >> a >> b >> c;
        q[a].push_back(c), q[b + 1].push_back(-c);
        disc[++ tot] = c;
    } 
    sort(disc + 1, disc + tot + 1);
    tot = unique(disc + 1, disc + tot + 1) - disc - 1;
    root[0] = build(1, tot);
    for(int i = 1; i <= n; i ++) {
        root[i] = root[i - 1];
        for(auto p : q[i]) root[i] = update(root[i], 1, tot, find(abs(p)), (p > 0 ? 1 : -1));
    }
    int lst = 1;
    for(int i = 1, x, b, c, a, k; i <= n; i ++) {
        cin >> x >> a >> b >> c;
        k = 1 + (a * lst + b) % c;
        if(k > dat[root[x]]) cout << (lst = sum[root[x]]) << '\n';
        else cout << (lst = query(root[x], 1, tot, k)) << '\n';
    }
    
    return 0;
}