[Daimayuan] 兔纸(C++,二分)

题目背景

鶸尛鱻养了许多兔纸。

题目描述

众所周知,兔纸是一种神奇的生物,它有许多种毛色。

鶸尛鱻一共有 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 x2 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 2n3×105 1 ≤ m , c o l i , c ≤ 3 × 1 0 5 1≤m,col_i,c≤3×10^5 1m,coli,c3×105,且保证 1 ≤ x < n 1≤x<n 1x<n 1 ≤ l < r ≤ n 1≤l<r≤n 1l<rn

附加说明

原题: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;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

WitheredSakura_

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值