实验任务
YellowStar 有一些历史账单,每个账单有两个属性,时间 \(t\) 和金额\(c\),现在他要把这些账单信息储存起来,但是由于业务繁忙,在储存过程中可能会发生查询,查询内容为:某一时间 \(t\) 之前(包括 \(t\) 时刻)金额为 \(c\) 的账单数量。对于每次查询,请你帮助Yellowstar 计算出数量。
数据输入
输入的第一行为数字 \(n\ (1\leq n\leq 10^5)\),表示事件数。
接下来 \(n\) 行,每行包含 3 个整数,\(tp_i, t_i, c_i\ (1 \leq tp_i\leq 2, 1\leq t_i, c_i\leq 10^9)\)。
\(tp_i = 1\) 时,表示 Yellowstar 插入一条时间为 \(t_i\), 价值为 \(c_i\)的账单信息;
\(tp_i = 2\) 时,表示查询时间在 \(t_i\) 之前(包括 \(t_i\) 时刻),价值为 \(c_i\)的账单数量。
数据输出
对于每次询问,输出一个整数表示答案。
输入示例 | 输出示例 |
---|---|
6 1 1 5 2 3 5 1 3 5 2 3 5 2 2 5 2 5 4 |
1 2 1 0 |
数据范围
\(80\%\)的得分点,\(n \leq 1000\);\(20\%\)的得分点,\(n \leq 100000\)。
源代码
1 |
|
设计思路与复杂度分析
考虑到本题金额属性无实际意义,故以离线方式处理,即以金额属性\(c\)相同的作为一批处理,这样即可去掉账单的金额属性\(c\)。操作1转变为每次插入一个值\(t_i\),操作2 转变为查询已经插入的数中值\(\leq t_i\) 的总数。
现在问题转变为一个单点更新,在线求前缀和的问题,可使用树状数组处理。但是树状数组要求\(t\)连续,一个策略是对\(t\)进行离散化,而更优的方法是为每一个输入添加一个\(cid\)属性,并以\(t\)为关键字排序,而处理的时候以\(cid\)的顺序处理。即插入的时候直接插入\(cid_i\),查询的时候查询\(\leq cid_i\)。
但是这样做之后处理顺序与输入顺序不一致,如果直接输出会导致输出顺序不正确,所以需要使用\(qid\)标记查询顺序,最后按照\(qid\)输出即可。
对于本题的情形,需要为每一个元素维护许多信息,这样直接对元素排序时移动元素的代价比较大。一个优化策略是使用下标排序:新建一个\(rank\) 数组,排完序之后\(rank_i\)表示的是排名为\(i\)的元素所在的位置。实现上只需以下三个步骤: 1. 初始化\(rank\)数组:for (int i=0; i<n; ++i) {rank[i]=i;}
; 2. 自定义以\(rank_i\)为下标的比较函数:bool cmp(int x, int y){return ci[x] < ci[y];}
; 3. 排序:sort(rank, rank + n, cmp)
。