说说我的思路
- 刚开始害怕内存不够开, 又是离散化又是动态开点,后来发现多虑了
- 按照时间轴建立线段树, 每个时刻用一颗线段树(值域)维护 优先级为 $p$ 的任务数量
- 对于任务安排, 从 $S$ ~ $E$ 有 优先级为 $p$ 的任务, 相当于这中间的的所有时刻的线段树的值域 $p$ 都要加 $1$
对应差分操作
$$ modify(root[S], p, + 1), modify(root[E + 1], p, - 1) $$
最后前只需要缀和一下, 即线段树合并,就将每个时刻的每个任务数量处理完毕了。
接下来只需要查询, 每次查询第 $x$ 时刻的前 $k$ 小的任务的和,做树上二分
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long i64;
const int N = 1e5 + 10;
int n, m;
struct segment {
int s, e, p;
}seg[N];
struct Node {
int l, r;
i64 cnt, v; // 区间任务数量, 和
}tr[N * 40];
int root[N], idx;
vector<int> alls;
int find(int x) {
return lower_bound(alls.begin(), alls.end(), x) - alls.begin() + 1;
}
void modify(int l, int r, int &u, int x, int k) {
if (!u) u = ++ idx;
if (l == r) {
tr[u].cnt += k;
tr[u].v = 1ll * alls[r - 1] * tr[u].cnt;
return ;
}
int mid = l + r >> 1;
if (x <= mid) modify(l, mid, tr[u].l, x, k);
else modify(mid + 1, r, tr[u].r, x, k);
tr[u].cnt = tr[tr[u].l].cnt + tr[tr[u].r].cnt;
tr[u].v = tr[tr[u].l].v + tr[tr[u].r].v;
}
void merge(int &x, int y) {
if (!x || !y) {
x |= y;
return ;
}
tr[x].cnt += tr[y].cnt;
tr[x].v += tr[y].v;
merge(tr[x].l, tr[y].l);
merge(tr[x].r, tr[y].r);
}
// 二分
i64 query(int l, int r, int u, int k) {
if (tr[u].cnt <= k) return tr[u].v;
if (l == r) return 1ll * alls[r - 1] * k;
int mid = l + r >> 1;
if (tr[tr[u].l].cnt >= k) return query(l, mid, tr[u].l, k);
else if (tr[tr[u].l].cnt + tr[tr[u].r].cnt >= k)
return query(l, mid, tr[u].l, k) + query(mid + 1, r, tr[u].r, k - tr[tr[u].l].cnt);
else return query(mid + 1, r, tr[u].r, k);
}
int main()
{
scanf("%d%d", &m, &n);
for (int i = 0; i < m; i ++ ) {
int s, e, p;
scanf("%d%d%d", &s, &e, &p);
seg[i] = {s, e, p};
alls.push_back(p);
}
sort(alls.begin(), alls.end());
alls.erase(unique(alls.begin(), alls.end()), alls.end());
for (int i = 0; i < m; i ++ ) {
int s = seg[i].s, e = seg[i].e, p = seg[i].p;
modify(1, alls.size(), root[s], find(p), 1);
modify(1, alls.size(), root[e + 1], find(p), -1);
}
for (int i = 1; i <= 100000; i ++ )
merge(root[i], root[i - 1]);
i64 pre = 1;
while (n -- ) {
int x, a, b, c;
scanf("%d%d%d%d", &x, &a, &b, &c);
int k = 1 + (1ll * a * pre + b) % c;
i64 res = query(1, alls.size(), root[x], k);
printf("%lld\n", res);
pre = res;
}
return 0;
}
if (l == r) return 1ll * alls[r - 1] * k;这行代码什么意思,看不懂
最后在一个边界节点上,他的个数可能 $>k$,而实际只取 $k$ 个
你是B站直播的那只老虎吗
哈?不是呀