任务查询系统
题目链接:ybt金牌导航4-6-5 / luogu P3168
题目大意
有一些数会出现,它会在一段时间内出现。
然后多个强制在线的询问,问某个时间中前 k 大的数的和。
思路
我们考虑一个时刻对于它前面的时刻有什么变化。
多了一些数,少了一些数。
而每个数整个过程中只会多一次少一次。
那我们就可以用主席树,让每个时刻都弄一个线段树。
然后每次就复制之前的,在修改多的和少的。
容易看到权值需要离散化。
(我手打离散化不知道为什么不行,调了一个下午,就用了自带的)
然后询问就在当前时刻查询。
还有如果查询的时候排名大于数个数,就输出当前所有数的和。
代码
#include<queue>
#include<cstdio>
#include<algorithm>
#define ll long long
#define y first
#define x second
using namespace std;
struct node {
int l, r, c, s;
}t[200001 << 6];
pair <int, int> p[200001];
int m, n, x, y, z, k, rt[200001];
int tot, maxval, b[200001];
ll pre = 1;
int abss(int x) {
if (x < 0) return -x;
return x;
}
void build(int &now, int l, int r) {
now = ++tot;
if (l == r) return ;
int mid = (l + r) >> 1;
build(t[now].l, l, mid);
build(t[now].r, mid + 1, r);
return ;
}
int copypoint(int bef) {
int re = ++tot;
t[re] = t[bef];
return re;
}
int insert(int bef, int l, int r, int pl, int x) {
int now = copypoint(bef);
if (l == r) {
t[now].c += x;
t[now].s += 1ll * x * b[pl];
return now;
}
int mid = (l + r) >> 1;
if (pl <= mid) t[now].l = insert(t[bef].l, l, mid, pl, x);
else t[now].r = insert(t[bef].r, mid + 1, r, pl, x);
t[now].c = t[t[now].l].c + t[t[now].r].c;
t[now].s = t[t[now].l].s + t[t[now].r].s;
return now;
}
ll get_sum(int now, int l, int r, int rnk) {
if (l == r) return 1ll * t[now].s / t[now].c * rnk;//记得这里要乘上的不一定是这个数的个数,而是你现在的 rnk
int mid = (l + r) >> 1;
if (rnk <= t[t[now].l].c) return get_sum(t[now].l, l, mid, rnk);
else return t[t[now].l].s + get_sum(t[now].r, mid + 1, r, rnk - t[t[now].l].c);
}
int main() {
// freopen("read.txt", "r", stdin);
scanf("%d %d", &m, &n);
for (int i = 1; i <= m; i++) {
scanf("%d %d %d", &p[(i << 1) - 1].y, &p[i << 1].y, &p[(i << 1) - 1].x);
p[i << 1].y++;
p[i << 1].x = -p[(i << 1) - 1].x;
b[i] = p[(i << 1) - 1].x;
}
sort(b + 1, b + m + 1);//离散化数的大小
maxval = unique(b + 1, b + m + 1) - b - 1;
build(rt[0], 1, maxval);
sort(p + 1, p + (m << 1) + 1);
int tmp = 1;
for (int i = 1; i <= (m << 1); i++) {//每个时间点建一个线段树(主席树来搞)
while (tmp < p[i].y) {
rt[tmp + 1] = rt[tmp];
tmp++;
}
if (tmp == m + 1) break;
int pp = lower_bound(b + 1, b + maxval + 1, abss(p[i].x)) - b;
rt[tmp] = insert(rt[tmp], 1, maxval, pp, p[i].x > 0 ? 1 : -1);
}
for (int i = 1; i <= n; i++) {
scanf("%d %d %d %d", &x, &k, &y, &z);
k = 1 + (1ll * k * pre + y) % z;
if (k >= t[rt[x]].c) pre = t[rt[x]].s;//超过当前的数的个数,直接是全部的和
else pre = get_sum(rt[x], 1, maxval, k);//在对应的线段树查询
printf("%lld\n", pre);
}
return 0;
}