1. 程式人生 > 實用技巧 >luoguP3939 數顏色

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\n", 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]);
		}
	}
}