[GDOI模拟2016.03.05]魔道研究

题目大意

给定 t 个可重集Ti,从第 i 个可重集中选择前i大,组成可重集 S
m个操作,分为两种,分别是给 t 集合删除某一个元素或是给t集合加入某一个元素。维护每次操作后 S 的前n大。

m,n,t300000 ,所有元素都为不大于 109 的正整数。


题目分析

显然我们可以使用数据结构维护 T 集合以及S集合。
添加操作时,我们在对应 Ti 集合中插入该数,查询排名 rank 。如果 rank<=i ,说明它挤掉了一个数,那么我们就将 Ti 集合的第 i+1 名从 S 中删除,最后在S加入新数即可。
删除操作类似。
这个数据结构可以选用平衡树,配对堆等。其实权值线段树就可以解决问题,具体过程需要动态开点。


代码实现

#include <iostream>
#include <cstring>
#include <cstdio>
#include <cctype>

using namespace std;

typedef long long LL;

int read()
{
    int x=0,f=1;
    char ch=getchar();
    while (!isdigit(ch))
    {
        if (ch=='-')
            f=-1;
        ch=getchar();
    }
    while (isdigit(ch))
    {
        x=x*10+ch-'0';
        ch=getchar();
    }
    return x*f;
}

const int S=40000005;
const int N=300005;
const int M=300005;
const int T=300005;
const int P=1000000000;

int n,m;

struct tree
{
    int son[S][2],size[S];
    int root[T];
    LL sum[S];
    int tot;

    int newnode()
    {
        size[++tot]=0;
        son[tot][0]=son[tot][1]=sum[tot]=0;
        return tot;
    }

    void init()
    {
        memset(size,0,sizeof size);
        memset(sum,0,sizeof sum);
        memset(son,0,sizeof son);
        tot=0;
        for (int i=0;i<=300000;i++)
            root[i]=newnode();
    }

    int kth(int rt,int rk,int l,int r)
    {
        if (l==r)
            return l;
        int mid=l+r>>1;
        if (size[son[rt][1]]>=rk)
            return kth(son[rt][1],rk,mid+1,r);
        rk-=size[son[rt][1]];
        return kth(son[rt][0],rk,l,mid);
    }

    int rank(int rt,int x,int l,int r)
    {
        if (l==r)
            return 1;
        int mid=l+r>>1;
        if (x>mid)
            return rank(son[rt][1],x,mid+1,r);
        else
            return size[son[rt][1]]+rank(son[rt][0],x,l,mid);
    }

    int change(int rt,int x,int l,int r,int delta)
    {
        if (!rt)
            rt=newnode();
        if (l==r)
        {
            sum[rt]+=delta*1ll*x;
            size[rt]+=delta;
            return rt;
        }
        int mid=l+r>>1;
        if (x<=mid)
            son[rt][0]=change(son[rt][0],x,l,mid,delta);
        else
            son[rt][1]=change(son[rt][1],x,mid+1,r,delta);
        int ls=son[rt][0],rs=son[rt][1];
        sum[rt]=sum[ls]+sum[rs];
        size[rt]=size[ls]+size[rs];
        return rt;
    }

    LL query(int rt,int kth,int l,int r)
    {
        if (l==r)
            return l*1ll*min(kth,size[rt]);
        int mid=l+r>>1;
        if (kth<=size[son[rt][1]])
            return query(son[rt][1],kth,mid+1,r);
        else
            return sum[son[rt][1]]+query(son[rt][0],kth-size[son[rt][1]],l,mid);
    }
}t;

int main()
{
    freopen("grimoire.in","r",stdin);
    freopen("grimoire.out","w",stdout);
    n=read(),m=read();
    t.init();
    for (int i=1;i<=m;i++)
    {
        char cmd=getchar();
        while (!isalpha(cmd))
            cmd=getchar();
        int ti=read(),pi=read();
        if (cmd=='B')
        {
            t.change(t.root[ti],pi,1,P,1);
            int rk=t.rank(t.root[ti],pi,1,P);
            if (rk<=ti)
            {
                if (t.size[t.root[ti]]>ti)
                {
                    int num=t.kth(t.root[ti],ti+1,1,P);
                    t.change(t.root[0],num,1,P,-1);
                }
                t.change(t.root[0],pi,1,P,1);
            }
            LL ans=t.query(t.root[0],n,1,P);
            printf("%lld\n",ans);
        }
        else
        {
            int rk=t.rank(t.root[ti],pi,1,P);
            t.change(t.root[ti],pi,1,P,-1);
            if (rk<=ti)
            {
                if (t.size[t.root[ti]]>=ti)
                {
                    int num=t.kth(t.root[ti],ti,1,P);
                    t.change(t.root[0],num,1,P,1);
                }
                t.change(t.root[0],pi,1,P,-1);
            }
            LL ans=t.query(t.root[0],n,1,P);
            printf("%lld\n",ans);
        }
    }
    fclose(stdin);
    fclose(stdout);
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值