リトルアルテムは、タイムマシンを発明しました!彼は時間にどこに行くことができるが、もちろん、すべての彼の考えは、コンピュータ科学です。:彼は、よく知られているデータ構造に、このタイムマシンを適用したい マルチセット。
アルテムは整数の基本的な多重集合を作成したいと考えています。彼は、これらの構造は、3種類の操作をサポートするために望んでいます:
- マルチセットに整数を追加します。セットと多重集合との差が多重集合一つの整数の複数のインスタンスを格納することができることであることに留意されたいです。
- マルチセットから整数削除します。これだけの整数の1つのインスタンスが削除されます。彼はその整数は、マルチセットで提示され、毎回の削除操作が呼び出されることを想定してアルテムは、任意の例外を処理する必要はありません。
- マルチセットに格納された整数のインスタンスの数を数えます。
しかし、タイムマシンについて何?アルテムは、単に1で多重集合1に操作を適用しません、彼は今、時間の異なる瞬間に移動し、そこに彼の操作を適用します。次の例を考えてみましょう。
- まずアルテムは、整数追加 で多重集合に5 時間の1番目の瞬間。
- そして、アルテムは、整数追加 現時点ではマルチセットに3 5。
- そして、アルテムは何尋ねる 5は、現時点では多重集合である 6答えは 1。
- 戻し時間に戻りアルテムとどのように多くの整数尋ねる 3は、現時点でセットである ので、4 3が瞬間のみを添加した 整数の数、5 瞬間3 に4つの等しい 0。
- その後、アルテムは再び時間に戻って削除し た瞬間にマルチセットから5 3。
- 最後にアルチョームは現時点で尋ねる どのように多くの整数7 5がセットにあります。結果は 、我々は削除しておりますので、0 の時点で5 3。
アルテムはそんなに彼が各変更後、彼はすべての削除操作のみ多重集合に存在する要素に適用されますことを保証するという例外を嫌うことに注意してください。第三のタイプの照会に対する回答はアルテムは、対応するクエリを行った瞬間に計算され、彼が作る未来の変化によって、どのような方法で影響を受けません。
ヘルプアルテムは、時間旅行者の多重集合を実装します。
入力の最初の行は、単一の整数含ま N (1≤ N アルテムのクエリの数- ≤100 000)。
そして、続く n個 のクエリの記述でラインを。それらの各々は、三つの整数含ま Iを、 tはIと し、 xはIを (1≤ A I ≤3、 1≤ T I、 X I ≤10 9) -これを実行するために、クエリの種類、時間アルテムのモーメントに移動それぞれのクエリおよびクエリ自体の価値、。時間のすべての瞬間がはっきりとし、各操作が適用された後、第1および第2のタイプのすべての操作が一致していることであることを保証しています。
各操作の出力を依頼するための整数のインスタンスの数は、時間の所与の瞬間に照会されます。
6
1 5
3 5
1 2 5
3 6 5
2 3 5
3 7 5
1
2
1
3
1 1 1
2 2 1
3 3 1
0
/ * 例一つの値だけのために、あなたは、位置(離散なるまでの時間)、時刻tにT + 1つの位置に追加された値のために瞬間にフェンウィックツリーを使用することができますトンtは位置にある時に値を削除します- 1は、時刻tにおけるクエリの値の数は、クエリ[1、T]の間隔は、その表示された 異なる値のため、値は、それぞれがフラッシュ操作一度フェンウィックツリーの値に仕上げ、離れて異なる行う必要がありますに注意しないこと記録される、空の値を追加するのmemset用いて、順次空にされる * / の#include <iostreamの> する#include <cstdioを> する#include <CStringの> する#include <アルゴリズム> の#define MAXN 100010 使用して 名前空間STDを、 INT N-、TR [MAXN * 4 ]、H [MAXN]; 構造体ノード{ int型のOP、T、ヴァル、ANS、ID; } A [MAXN]; INT ST [MAXN]、トップ、デル[MAXN]; BOOL CMP(ノードU、ノードV ){ IF(!u.val = v.val)リターン u.val < v.val。 返す u.id < v.id。 } ブールCMPP(ノードU、ノードv){ リターン u.id < v.id。 } INT Qry_tarr(INT POS){ int型の和= 0 。 一方(POS){ 合計 + = TR [POS]。 POS - =(POS ^(POS- 1))&POS。 } 戻り値の和。 } ボイド Add_tarr(int型の POS、INT デルタ){ 一方(POS <=N){ TR [POS] + = デルタ。 POS + =(POS ^(POS- 1))&POS。 } } int型のmain(){ scanf関数(" %のD "、&N) 以下のために(INT iが= 1 ; I <= N; I ++ ){ scanf関数(" %D%D%D "、および[I] .OP、&[I] .T、および[I] .val)。 H [I] = [I] .T。 [I] .ID = I。 } ソート(H + 1、H + N + 1 )。 にとって(INTは iは= 1 ; I <= N; I ++ ){ [I] .T = LOWER_BOUND(H + 1、H + N + 1、[I] .T) - H。 } ソート( + 1、+ N + 1 、CMP)。 INT valnow = A [ 1 ] .val。 以下のために(INT iが= 1 ; I <= N; I ++ ){ 場合([I] .val =!valnow){ 一方(上部){ Add_tarr(ST [トップ]、デル[トップ])。 トップ - ; } valnow= [I] .val。 } もし(== [I] .OP 1 ){ ST [ ++トップ] = [I] .T。 デル[トップ] = - 1 。 Add_tarr([I] .T、1 )。 } 他の 場合([i]が.OP == 2 ){ ST [ ++トップ] = [I] .T。 デル[トップ] = 1 。 Add_tarr([I] .T、 - 1 )。 } 他{ [I] .ans = Qry_tarr([I] .T)。 } } ソート( + 1、+ N + 1 、CMPP)。 以下のために(INT iが= 1 ; I <= N; I ++ ){ 場合([i]が.OP == 3 ){ のprintf(" %D \ n " 、[I] .ans)。 } } 戻り 0 。 }