魔道研究

Description

“我希望能使用更多的魔法。不对,是预定能使用啦。最终我要被大家称呼为大魔法使。为此我决定不惜一切努力。”
——《The Grimoire of Marisa》雾雨魔理沙
魔理沙一如既往地去帕秋莉的大图书馆去借魔导书(Grimoire) 来学习魔道。
最开始的时候,魔理沙只是一本一本地进行研究。然而在符卡战中,魔理沙还是战不过帕秋莉。
好在魔理沙对自己的借还和研究结果进行了记录,从而发现了那些魔导书的精妙之处。
帕秋莉的那些魔导书,每本都有一个类别编号ti 和威力大小pi。而想要获得最有威力的魔法,就必须同时研究一些魔导书。而研究的这些魔导书就必须要满足,类别编号为T 的书的本数小于等于T,并且总共的本数小于等于一个给定的数N。而研究这些魔导书之后习得的魔法的威力就是被研究的魔导书的威力之和。
为了击败帕秋莉,魔理沙想要利用自己发现的规律来获得最有威力的魔法。
她列出了计划中之后M 次的借还事件,并想要知道每个事件之后自己所能获得的魔法的最大威力。可她忙于魔法材料——蘑菇的收集,于是这个问题就交给你来解决了。

Solution

这道题,一眼就是数据结构维护。
那需要查找第k大值,求和,删除,加入,那么明显的就有数据结构可以维护了:1、线段树(或主席树);2、splay;3、treap;4、替罪羊树(表示并不会)
我打了一颗线段树,打法十分显然。

Code

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#define fo(i,a,b) for(i=a;i<=b;i++)
#define ll long long
const int maxn=300007;
const int da=1000000007;
using namespace std;
int i,j,k,l,tt,n,m,p,num;
ll ans,ans1,ans2;
int root[maxn];
struct node{
    int tot,l,r;
    ll sum;
}t[maxn*92];
char s[10];
void insert(int &v,int l,int r,int z){
    if(!v)v=++num;
    t[v].sum+=z;
    t[v].tot+=1;
    if(l==r)return;
    int mid=(l+r)/2;
    if(z<=mid) insert(t[v].l,l,mid,z);
    else insert(t[v].r,mid+1,r,z);
}
void delet(int v,int l,int r,int z){
    if(!v)v=++num;
    t[v].sum-=z;
    t[v].tot-=1;
    if(l==r)return;
    int mid=(l+r)/2;
    if(z<=mid) delet(t[v].l,l,mid,z);
    else delet(t[v].r,mid+1,r,z);
}
void dikda(int x,int l,int r,int z){
    if(l==r){
        ans1=l;
        return;
    }
    int mid=(l+r)/2;
    if(t[t[x].r].tot>=z)dikda(t[x].r,mid+1,r,z);
    else dikda(t[x].l,l,mid,z-t[t[x].r].tot);
}
void get(int x,int l,int r,int z){
    if(l==r){
        ans=ans+min(t[x].tot,z)*l;
        return;
    }   
    int mid=(l+r)/2;
    if(t[t[x].r].tot>=z)get(t[x].r,mid+1,r,z);
    else get(t[x].l,l,mid,z-t[t[x].r].tot),ans+=t[t[x].r].sum;
}
int main(){
    //freopen("grimoire.in","r",stdin);
    //freopen("grimoire.out","w",stdout);
    scanf("%d%d",&n,&m);
    fo(i,1,m){
        scanf("%s%d%d",s,&tt,&p);
        if(s[0]=='B'){
            if(t[root[tt]].tot>=tt){
                dikda(root[tt],1,da,tt);
            }
            else ans1=0;
            if(p>=ans1){
                insert(root[0],1,da,p);
                if(ans1>0)delet(root[0],1,da,ans1);
            }
            insert(root[tt],1,da,p);
        }
        else{
            if(t[root[tt]].tot>tt){
                dikda(root[tt],1,da,tt+1);
            }
            else ans1=0;
            if(p>=ans1){
                delet(root[0],1,da,p);
                if(ans1>0)insert(root[0],1,da,ans1);
            }
            delet(root[tt],1,da,p);
        }
        ans=0;
        get(root[0],1,da,n);
        printf("%lld\n",ans);
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值