题目背景
鶸尛鱻养了许多兔纸。
题目描述
众所周知,兔纸是一种神奇的生物,它有许多种毛色。
鶸尛鱻一共有 n n n 只兔纸,它们从左到右排成一排,第 i i i 只兔纸的毛色为 c o l i col_i coli。
兔纸们十分活跃,时常会与旁边的兔纸交换位置。
五颜六色的兔纸弄得鶸尛鱻眼花缭乱,他想统计区间 [ l , r ] [l,r] [l,r] 内兔纸的颜色,但他的数学很差,你可以帮帮他吗?
输入格式
第 1 1 1 行两个正整数 n , m n,m n,m 分别表示兔纸数量和操作次数。
第 2 2 2 行 n n n 个正整数 c o l 1 , c o l 2 , … , c o l n col_1,col_2,…,col_n col1,col2,…,coln,表示初始状态下从左到右每只兔纸的颜色。
此后
m
m
m 行,每行输入格式为 1 x
或 2 l r c
,其中
{
1
/
2
}
\{1/2\}
{1/2} 表示操作类型。
操作 1 1 1 为 交换 操作,表示第 x x x 只兔纸和第 x + 1 x+1 x+1 只兔纸交换位置。
操作 2 2 2 为 查询 操作,询问当前区间 $[l,r] $内有多少只颜色为 c c c 的兔纸。
输出格式
对于每个操作 2 2 2,你需要输出一行一个自然数作为答案。
样例输入
7 9
8 7 6 6 7 8 3
1 5
1 4
2 1 7 7
1 6
1 4
2 3 6 8
2 1 3 7
2 1 2 6
2 2 5 7
样例输出
2
1
1
0
1
数据范围
对于全部测试数据,满足 2 ≤ n ≤ 3 × 1 0 5 2≤n≤3×10^5 2≤n≤3×105, 1 ≤ m , c o l i , c ≤ 3 × 1 0 5 1≤m,col_i,c≤3×10^5 1≤m,coli,c≤3×105,且保证 1 ≤ x < n 1≤x<n 1≤x<n, 1 ≤ l < r ≤ n 1≤l<r≤n 1≤l<r≤n。
附加说明
原题:P3939 数颜色
解题思路
最开始看到题目描述的时候,第一反应是用可持久化线段树来实现。
但是还有另外一种更简单快速的方法:二分(lower_bound()
和upper_bound()
)。
这里只介绍代码的逻辑(因为算法比较emm神奇,很难说怎么想出来的):
采用vector
容器,为每一种颜色维护一个容器。
每一个容器中维护着所有该种颜色兔纸的位置。
这样每一只兔纸都有两个标识:1)在队列中的位置;2)在容器中的索引。
交换操作就是将兔纸
x
x
x位置++
,将兔纸
x
+
1
x+1
x+1位置--
。
如果要交换的两个兔纸位置相同则不进行任何操作,这样可以保证vector
容器中的元素始终具有单调性。
查询操作是整个算法最巧妙的地方:
使用lower_bound()
和upper_bound()
获取指定范围
[
l
,
r
]
[l,r]
[l,r]内最左边兔纸的索引和最右边兔纸的索引。
两个索引的差值 + 1 +1 +1即为答案。
最后,AC代码如下:
#include <iostream>
#include <vector>
using namespace std;
const int max_n = 3e5;
const int max_c = 3e5;
int rabbits[max_n + 1];
vector<int>pos[max_c + 1];
int main() {
int n, m;
cin >> n >> m;
for (int i = 1; i <= n; i++) {
cin >> rabbits[i];
pos[rabbits[i]].push_back(i);
}
int select, x, l, r, c;
while (m--) {
cin >> select;
if (select == 1) {
cin >> x;
if (rabbits[x] == rabbits[x + 1]) continue;
int col_1 = rabbits[x], col_2 = rabbits[x + 1];
swap(rabbits[x], rabbits[x + 1]);
int ret_1 = lower_bound(pos[col_1].begin(), pos[col_1].end(), x) - pos[col_1].begin();
pos[col_1][ret_1]++;
int ret_2 = lower_bound(pos[col_2].begin(), pos[col_2].end(), x + 1) - pos[col_2].begin();
pos[col_2][ret_2]--;
}
else {
cin >> l >> r >> c;
int ret_1 = lower_bound(pos[c].begin(), pos[c].end(), l) - pos[c].begin();
int ret_2 = upper_bound(pos[c].begin(), pos[c].end(), r) - pos[c].begin() - 1;
if (ret_1 > ret_2) cout << 0 << endl;
else cout << ret_2 - ret_1 + 1 << endl;
}
}
return 0;
}