[权值线段树]魔道研究

题目描述

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

Input
输入文件grimoire.in。
第1 行2 个整数N,M,分别表示魔理沙能研究的魔导书本数的上限和她的借还事件数。
之后M 行,每行的形式为“op t p”(不含引号)。Op 为“BORROW” 或“RETURN”,分别表示借书和还书。T 为一个整数,表示这本书的类别编号。P为一个整数,表示这本书的威力大小。注意,还书时如果有多本书满足类别编号为t,威力大小为p,这表明这些书都是相同的,魔理沙会任选其中一本书还回去。如果你问我为何会有相同的书,多半因为这是魔导书吧。

Output
输出文件grimoire.out。
一共M 行,每行一个整数,即每个事件之后的最大威力。

Sample Input
5 10
BORROW 1 5811
BORROW 3 5032
RETURN 3 5032
BORROW 3 5550
BORROW 5 3486
RETURN 1 5811
RETURN 3 5550
BORROW 4 5116
BORROW 3 9563
BORROW 5 94

Sample Output
5811
10843
5811
11361
14847
9036
3486
8602
18165
18259

Data Constraint
对于5% 的数据,1 <= t,N,M <= 50。
对于10% 的数据,1 <= t,N,M <= 100。
对于30% 的数据,1 <= t,N,M<= 10 000。
另有30% 的数据,1 <= p <= 1 000。
对于100% 的数据,1 <= t,N,M <= 300 000,1<= p<= 1 000 000 000。
另外,总共有30% 的数据,满足没有“RETURN” 操作。这部分数据均匀分布。

分析

这题就是有几个集合,一个大集多个小集,然后往大集里面添加集合i里面的前ti大,每次进行完操作把大集的前n大总和输出
这题嘛,因为要求区间第k大,有多种打法(据说正解对顶堆?因为对顶堆才是NOIP难度的)我选择了主席树(显然我又放弃了)权值线段树来做这个东西
为了维护多个集合,我用root[x]表示某集合线段树的根
节省空间,动态开点
当加入时,有两种情况:
大集未满n个,直接加入(外加一点点条件)
大集满n个,找出自己区间的第t大,比较大小,大的插入大集,小的退回小集
但是权值线段树我们可以把集合不满时的第k大搞成0,就变成一种情况:
获取小集中第k大,比较大小,替换进入大集
删除时类似,不过把第k大变成第k+1大而已。

#include <iostream>
#include <cstdio>
#include <cstring>
#define rep(i,a,b) for (i=a;i<=b;i++)
typedef long long ll;
const int N=300001*100;
const ll oo=1000000000;
using namespace std;
int root[N/100];
int c[N][2],cnt;
int n,m;
ll info[N][2],ans;

void Add(int &rt,int l,int r,int key,int zf) {
    if (!rt) rt=++cnt;
    info[rt][0]+=zf,info[rt][1]+=zf*key;
    if (l==r) return;
    int mid=(l+r)>>1;
    if (key<=mid) Add(c[rt][0],l,mid,key,zf);
    else Add(c[rt][1],mid+1,r,key,zf);
}

int Ask(int rt,int l,int r,ll kth) {
    if (l==r) {
        ans+=min(kth,info[rt][0])*l;
        return l;
    }
    int mid=(l+r)>>1;
    if (kth<=info[c[rt][1]][0]) return Ask(c[rt][1],mid+1,r,kth);
    else {
        ans+=info[c[rt][1]][1];
        return Ask(c[rt][0],l,mid,kth-info[c[rt][1]][0]);
    }
}

int main() {
    int i;
    char c[10];
    scanf("%d%d\n",&n,&m);
    rep(i,1,m) {
        int t,p;
        scanf("%s",c+1);
        scanf("%d%d",&t,&p);
        if (c[1]=='B') {
            int kth=Ask(root[t],0,oo,t);
            Add(root[t],0,oo,p,1);
            if (p>kth) {
                Add(root[0],0,oo,p,1);
                Add(root[0],0,oo,kth,-1);
            }
        }
        else {
            int kth=Ask(root[t],0,oo,t+1);
            Add(root[t],0,oo,p,-1);
            if (p>=kth) {
                Add(root[0],0,oo,p,-1);
                Add(root[0],0,oo,kth,1);
            }
        }
        ans=0;
        Ask(root[0],0,oo,n);
        printf("%lld\n",ans);
    }
}

猜你喜欢

转载自blog.csdn.net/ssl_qyh0ice/article/details/81021355
今日推荐