[GDOI模拟2016.03.05]魔道研究

来源:互联网 发布:js blob uint8array 编辑:程序博客网 时间:2024/06/02 11:19

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);    }}
0 0
原创粉丝点击