Little Artem and Time Machine
巨弱第一次写题解,自己都看不懂
Little Artem has invented a time machine! He could go anywhere in time, but all his thoughts of course are with computer science. He wants to apply this time machine to a well-known data structure: multiset.
Artem wants to create a basic multiset of integers. He wants these structure to support operations of three types:
- Add integer to the multiset. Note that the difference between set and multiset is that multiset may store several instances of one integer.
- Remove integer from the multiset. Only one instance of this integer is removed. Artem doesn’t want to handle any exceptions, so he assumes that every time remove operation is called, that integer is presented in the multiset.
- Count the number of instances of the given integer that are stored in the multiset.
But what about time machine? Artem doesn’t simply apply operations to the multiset one by one, he now travels to different moments of time and apply his operation there. Consider the following example.
- First Artem adds integer $ 5 $ to the multiset at the $ 1 $ -st moment of time.
- Then Artem adds integer $ 3 $ to the multiset at the moment $ 5 $ .
- Then Artem asks how many $ 5 $ are there in the multiset at moment $ 6 $ . The answer is $ 1 $ .
- Then Artem returns back in time and asks how many integers $ 3 $ are there in the set at moment $ 4 $ . Since $ 3 $ was added only at moment $ 5 $ , the number of integers $ 3 $ at moment $ 4 $ equals to $ 0 $ .
- Then Artem goes back in time again and removes $ 5 $ from the multiset at moment $ 3 $ .
- Finally Artyom asks at moment $ 7 $ how many integers $ 5 $ are there in the set. The result is $ 0 $ , since we have removed $ 5 $ at the moment $ 3 $ .
Note that Artem dislikes exceptions so much that he assures that after each change he makes all delete operations are applied only to element that is present in the multiset. The answer to the query of the third type is computed at the moment Artem makes the corresponding query and are not affected in any way by future changes he makes.
Help Artem implement time travellers multiset.
输入格式
The first line of the input contains a single integer $ n $ ( $ 1<=n<=100000 $ ) — the number of Artem’s queries.
Then follow $ n $ lines with queries descriptions. Each of them contains three integers $ a_{i} $ , $ t_{i} $ and $ x_{i} $ ( $ 1<=a_{i}<=3 $ , $ 1<=t_{i},x_{i}<=10^{9} $ ) — type of the query, moment of time Artem travels to in order to execute this query and the value of the query itself, respectively. It’s guaranteed that all moments of time are distinct and that after each operation is applied all operations of the first and second types are consistent.
输出格式
For each ask operation output the number of instances of integer being queried at the given moment of time.
样例 #1
样例输入 #1
6
1 1 5
3 5 5
1 2 5
3 6 5
2 3 5
3 7 5
样例输出 #1
1
2
1
样例 #2
样例输入 #2
3
1 1 1
2 2 1
3 3 1
样例输出 #2
0
题面翻译
给你一个带时间戳的 multiset,进行 n n n 次操作
-
在 t t t 时刻插入一个 x x x。
-
在 t t t 时刻删除一个 x x x。
-
询问在 t t t 时刻有几个 x x x。
操作按照给的顺序进行。
操作形如 q t x
,q
表示操作的类型。
1 ≤ n ≤ 1 0 5 1\leq n\leq 10^5 1≤n≤105, 1 ≤ t , x ≤ 1 0 9 1\leq t,x\leq 10^9 1≤t,x≤109。
思路
可以用树状数组解答
由题意得不同的x之间不会影响结果,所以先将x进行分组排序,x为第一关键字,操作点id为第二关键字排序
每一组单独计算
可以把操作1看为在时刻t时x的数量+1,把操作2看为在时刻t时x的数量-1,在操作3查询时只需要查询在时刻t时x的数量,树状数组的下标即时刻t,因为查询操作是查询在t时刻时x的数量即树状数组t这一点的值,所以只需要修改数组中t时刻之后的值
例如样例1中第一步操作后树状数组为{0, 1, 1, 0, 1, 0, 0, 0, 1, 0, 0}
表示在t及t之后x的数量
第二步查询5时刻时即输出1
第三步在时刻2加入一个x,即t时刻后x的数量+1,数组变为{0, 1, 2, 0, 2, 0, 0, 0, 2, 0, 0}
第四步查询时6时即输出2
第五步在时刻3时删除一个x即将数组中时刻3之后的数量-1,数组变为{0, 1, 2, 0, 1, 0, 0, 0, 1, 0, 0}
查询时只需查询当前时刻的值即可
其他同理
但是直接用t进行操作显然是不行的,t <= 1e9
所以将t离散化,先将t排序,再求出t在全部t中的位置。
代码
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef struct Node{
ll id, op, t, x;
bool operator < (const Node &a) const{
if (x == a.x) return id < a.id;
return x < a.x;
}
}node;
const ll N = 1e5 + 10;
node arr[N];
ll c[N], ans[N], b[N];
ll n;
ll lowbit(ll x){
return x & (-x);
}
void add(ll i, ll k)
{
while (i <= n)
{
c[i] += k;
i += lowbit(i);
}
}
ll sum(ll i)
{
ll res = 0;
while (i)
{
res += c[i];
i -= lowbit(i);
}
return res;
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
cin >> n;
memset(ans, -1, sizeof(ans));
for (ll i = 1; i <= n; i++)
{
cin >> arr[i].op >> arr[i].t >> arr[i].x;
arr[i].id = i;
b[i] = arr[i].t;
}
sort(arr + 1, arr + 1 + n);
sort(b + 1, b + 1 + n);
for (ll i = 1; i <= n; i++)
{
arr[i].t = lower_bound(b + 1, b + 1 + n, arr[i].t) - b;
}
ll it = 1;
while (it <= n) //分组操作
{
ll mem = it;
while (it <= n && arr[it].x == arr[mem].x)
{
if (arr[it].op == 1)
{
add(arr[it].t, 1); //数量+1
}
else if (arr[it].op == 2)
{
add(arr[it].t, -1); //数量-1
}
else
{
ans[arr[it].id] = sum(arr[it].t); //保存查询的值
}
it++;
}
it = mem; //撤销操作,因为每组不同的x都需要重置数组,但是memset复杂度过高会炸复杂度,所以采用撤销操作的方式
while (it <= n && arr[it].x == arr[mem].x)
{
if (arr[it].op == 1)
{
add(arr[it].t, -1);
}
else if (arr[it].op == 2)
{
add(arr[it].t, 1);
}
it++;
}
}
for (ll i = 1; i <= n; i++)
{
if (~ans[i]) cout << ans[i] << endl;
}
return 0;
}