luoguP3939 数颜色

传送门

题解

懒得写题,又不想颓,所以开始水题解。

大意就是维护一个序列,要求支持两种操作:一是在给出的区间内查询某个值的出现次数,二是交换相邻某两个的元素。

一看到区间操作就想到了线段树...于是开始打主席树,打到一半发现不会打...又改成普通线段树,打完后发现比暴力还慢上好几倍...主要是颜色可能有很多种,而且相当分散,线段树的区间效果体现不出来...

然后这道题还很是毒瘤地卡了主席树,ST表...甚至它连分块都卡了...(出题人这样恶意满满不会折阳寿吗)

于是,面对3e5的数据,数据结构已经GG了,还有什么能够拯救我们呢?没错,那就是我们的STL!(STL大法好)(然鹅上一题的lower_bound就被卡飞了)

因为颜色的种类很多,分布又很稀疏,用数组来存储是不可能的,但是,我们有伟大的(vector)

直接反手开一个vector,(vector[i])表示(i)这个颜色依次出现的下标,就算你数据再怎么毒瘤,只要我动态开内存不就好了吗,反正就算是由于(vector)的特性它最多也就6e5的内存,完全够了。另外由于我们在读入时直接记录,所以它一定是满足单调上升的,所以在查找时可以二分优化一下。

具体的实现参见代码吧。

#include <bits/stdc++.h>
using namespace std;
#define ll long long
char buf[1 << 20], *p1, *p2;
char getc() {
	if(p1 == p2) {
		p1 = buf;
		p2 = buf + fread(buf, 1, 1 << 20, stdin);
		if(p1 == p2) return EOF;
	}
	return *p1++;
}
inline ll read() {
	ll s = 0, w = 1; char c = getc();
	while(c < '0' || c >'9') {if(c == '-')w = -1; c = getc();}
	while(c >= '0' && c <= '9') s = s * 10 + c - '0', c = getc();
	return s * w;
}
const int maxn = 3e5 + 100;
int n, m;
int a[maxn];
vector<int> col[maxn];
int main() {
	n = read(), m = read();
	for(register int i = 1; i <= n; i++) {
		a[i] = read();
		col[a[i]].push_back(i);
	}
	int op, x, y, z;
	int it1, it2;
	for(register int i = 1; i <= m; i++) {
		op =  read();
		if(op == 1) {
			x = read(), y = read(), z = read();
			it1 = lower_bound(col[z].begin(), col[z].end(), x) - col[z].begin();
			it2 = upper_bound(col[z].begin(), col[z].end(), y) - 1 - col[z].begin();
			printf("%d
", it2 - it1 + 1);
		}
		else {
			x = read();
			if(a[x] == a[x + 1]) continue;
			it1 = lower_bound(col[a[x]].begin(), col[a[x]].end(), x) - col[a[x]].begin();
			col[a[x]][it1]++;
			it2 = lower_bound(col[a[x + 1]].begin(), col[a[x + 1]].end(), x + 1) - col[a[x + 1]].begin();
			col[a[x + 1]][it2]--;
			swap(a[x], a[x + 1]);
		}
	}
}
原文地址:https://www.cnblogs.com/Zfio/p/13428086.html