题目大意:
有若干个可重集合,然后我们从第
个可重集合中拿前
大组成一个新的可重集合
。我们的目的是动态维护
的前
大的和。
给出数
,有
次操作,每次会插入一个数
进入集合
中或者在集合
中删去原有的一个数
,然后回答
中的前
大的和,不足
时直接求当前
的总和。
分析:
我们可以维护所有的小集合以及
。
维护
棵权值线段树,第
棵线段树对应第
个小集合,
另外维护一棵权值线段树,对应
。
对于插入操作,即在树
中插入
。
可以通过线段树
的进行单点修改
然后,
对于要不要将
加入 S 中。
这个只要知道
在集合
中排名是否
第
大,
而对于第
大,我们也可以通过线段树得到
然后我们判断
如果
第
大,那么插入进
中,并将原先在
的集合
的第
大的数弹出。
然后对于所有的线段树,我们都要动态开节点,也就是说我们不把那些空节点建起来,只保留那些非空的节点。
我们描述一个可重集合里面的元素,可以开一个以权值为下标的数组,数组内容为对应权值的出现次数。这个数组称为权值数组,而用来维护权值数组的线段树,就是权值线段树。
然后,求某个权值在集合里的排名什么的,只要在线段树里查查前缀和就知道了。
如我们现在站在线段树的某个节点
上,我们要求权值在这个区间里的第
大元素。
①
,显然直接得到为
。
②
,我们可以先看一看这个点的右儿子
。
若
中元素个数
,那第
大元素显然在
中,在右儿子求解。
若
中的元素个数
,那么第 k 大元素显然在
,那么我们令
减去
中的元素个数,减完后的
设为
,
那么我们现在就是要求
中的第
大,那么我们在左儿子求解。
代码:
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#define fo(i, j, k) for (int i = j; i <= k; i++)
#define N 300005
#define M 50
using namespace std;
const int inf = 1e9;
typedef long long ll;
int n, m, tot, root[N], num[N*M], lson[N*M], rson[N*M];
ll ans,sum[N*M];
char s[10];
void Insert(int &x, int l, int r, ll k, bool flag) {
if(!x) x = ++tot;
if (flag) num[x]++, sum[x] += k;
else num[x]--, sum[x] -= k;
if (l == r) return;
int mid = (l + r) >> 1;
if (k <= mid) Insert(lson[x], l, mid, k, flag);
else Insert(rson[x], mid+1, r, k, flag);
}
int Get_Ranknum(int x, int l, int r, int k) {
if (l == r) {
ans += min(k, num[x]) * l;
return l;
}
int mid = (l + r) >> 1;
if (num[rson[x]] >= k)
return Get_Ranknum(rson[x], mid+1, r, k);
ans += sum[rson[x]];
return Get_Ranknum(lson[x], l, mid, k - num[rson[x]]);
}
int main() {
freopen("grimoire.in","r",stdin);
freopen("grimoire.out","w",stdout);
scanf("%d %d", &n, &m);
int x; ll y;
fo(i, 1, m) {
scanf("%s", s);
if (s[0] == 'B') {
scanf("%d %lld", &x, &y);
int k = Get_Ranknum(root[x], 0, inf, x);
Insert(root[x], 0, inf, y, 1);
if (y >= k)
Insert(root[0], 0, inf, y, 1),
Insert(root[0], 0, inf, k, 0);
}
else {
scanf("%d %lld", &x, &y);
int k = Get_Ranknum(root[x], 0, inf, x+1);
Insert(root[x], 0, inf, y, 0);
if (y >= k)
Insert(root[0], 0, inf, y, 0),
Insert(root[0], 0, inf, k, 1);
}
ans = 0;
Get_Ranknum(root[0], 0, inf, n);
printf("%lld\n", ans);
}
return 0;
}