题目大意:
有若干个可重集合,然后我们从第
i
i
个可重集合中拿前 大组成一个新的可重集合
S
S
。我们的目的是动态维护 的前
N
N
大的和。
给出数,有
M
M
次操作,每次会插入一个数进入集合
t
t
中或者在集合中删去原有的一个数
p
p
,然后回答中的前
N
N
大的和,不足时直接求当前
S
S
的总和。
分析:
我们可以维护所有的小集合以及
S
S
。
维护棵权值线段树,第
i
i
棵线段树对应第个小集合,
另外维护一棵权值线段树,对应
S
S
。
对于插入操作,即在树中插入
p
p
。
可以通过线段树
n)
n
)
的进行单点修改
然后,
对于要不要将
p
p
加入 S 中。
这个只要知道 在集合
t
t
中排名是否第
t
t
大,
而对于第大,我们也可以通过线段树得到
然后我们判断
p
p
如果第
t
t
大,那么插入进中,并将原先在
S
S
的集合的第
t
t
大的数弹出。
然后对于所有的线段树,我们都要动态开节点,也就是说我们不把那些空节点建起来,只保留那些非空的节点。
我们描述一个可重集合里面的元素,可以开一个以权值为下标的数组,数组内容为对应权值的出现次数。这个数组称为权值数组,而用来维护权值数组的线段树,就是权值线段树。
然后,求某个权值在集合里的排名什么的,只要在线段树里查查前缀和就知道了。
如我们现在站在线段树的某个节点
[l,r]
[
l
,
r
]
上,我们要求权值在这个区间里的第
k
k
大元素。
①,显然直接得到为
l
l
。
②,我们可以先看一看这个点的右儿子
[mid+1,r]
[
m
i
d
+
1
,
r
]
。
若
[mid+1,r]
[
m
i
d
+
1
,
r
]
中元素个数
≥k
≥
k
,那第
k
k
大元素显然在 中,在右儿子求解。
若
[mid+1,r]
[
m
i
d
+
1
,
r
]
中的元素个数
<k
<
k
<script type="math/tex" id="MathJax-Element-51">< k</script>,那么第 k 大元素显然在
[l,mid]
[
l
,
m
i
d
]
,那么我们令
k
k
减去 中的元素个数,减完后的
k
k
设为,
那么我们现在就是要求
[l,mid]
[
l
,
m
i
d
]
中的第
x
x
<script type="math/tex" id="MathJax-Element-58">x</script>大,那么我们在左儿子求解。
代码:
#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;
}