Description
给出T个集合,和m个操作,一开始所有集合皆为空。每个操作有两种形式,一是在t集合里加入一个元素x,一种是在t集合里删除一个元素x(保证x存在)。然后从每个集合t中选出前t大的元素加入另一个大集合s中,求s中的前n大元素和。
m,n,t≤300000,所有元素都为不大于109的正整数。
Solution
很明显的数据结构题,关键是要如何维护。想到区间前k大,马上就想到了主席树这种神奇的东西。然而这道题带修改!仔细观察发现其实并不需要主席树的前缀和模式,所以把主席树中的每一颗树分开来就行了。每次操作在单独的一棵线段树上修改。然后判断这个操作对前K大有没有影响,如果有影响就在总的那棵线段树上修改,然后就能过了。
注意动态开节点。
Code
#include<cstdio>
#include<cstring>
#include<algorithm>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define maxt 1000000007
#define N 300005
#define ll long long
using namespace std;
struct note{
int tot,l,r;ll sum;
}t[N*92];
int root[N],n,m,x,y,k,tot;
ll ans;
char s[6];
void add(int &v,int l,int r,int x,int y) {
if (!v) v=++tot;
t[v].sum+=x*y;t[v].tot+=y;
if (l==r) return;
int m=(l+r)/2;
if (x<=m) add(t[v].l,l,m,x,y);
else add(t[v].r,m+1,r,x,y);
}
void find(int v,int l,int r,int x) {
if (l==r) {k=l;return;}
int m=(l+r)/2;
if (t[t[v].r].tot>=x) find(t[v].r,m+1,r,x);
else find(t[v].l,l,m,x-t[t[v].r].tot);
}
void getans(int v,int l,int r,int x) {
if (l==r) {ans+=min(x,t[v].tot)*l;return;}
int m=(l+r)/2;
if (t[t[v].r].tot>=x) getans(t[v].r,m+1,r,x);
else ans+=t[t[v].r].sum,getans(t[v].l,l,m,x-t[t[v].r].tot);
}
int main() {
scanf("%d%d",&n,&m);
fo(i,1,m) {
scanf("%s%d%d",s,&x,&y);
if (s[0]=='B') {
if (t[root[x]].tot>=x) find(root[x],1,maxt,x);
else k=0;
if (y>=k) {
add(root[0],1,maxt,y,1);
if (k) add(root[0],1,maxt,k,-1);
}
add(root[x],1,maxt,y,1);
} else {
if (t[root[x]].tot>x) find(root[x],1,maxt,x+1);
else k=0;
if (y>=k) {
add(root[0],1,maxt,y,-1);
if (k) add(root[0],1,maxt,k,1);
}
add(root[x],1,maxt,y,-1);
}
ans=0;getans(root[0],1,maxt,n);
printf("%lld\n",ans);
}
}