[XJOI NOI2015模擬題13] A 神奇的矩陣 【分塊】

題目連接:XJOI NOI2015-13 Aios

 

題目分析

首先,題目定義的這種矩陣有一個神奇的性質,第 4 行與第 2 行相同,因而第 5 行也就與第 3 行相同,後面的也是同樣。數組

所以矩陣能夠看作只有 3 行,從上到下就是 1 2 3 2 3 2 3 ......spa

而後咱們使用分塊,將每一行分紅 sqrt(m) 大小的塊。blog

而後維護 A[i][j] —— 第一行前 i 塊中,數字 j 的出現次數。get

同時維護 B[i][j] —— 第二行前 i 塊中,數字 j 的出現次數。string

這裏要將第一行的數字進行離散化減少 j 的範圍。(同時要注意,詢問第一行的數字時,不要直接輸出了離散化以後的數字QAQ,要輸出本來的數字,我就是這麼WA的it

而後對於詢問第二行的 x 位置,就先加上第一行 [1, x] 中前面的整個 k 塊中這個數字的個數,再 O(sqrt n) 枚舉最後一個塊中前面到 x 的一段。io

對於詢問第三行的 x 位置,先計算第二行 x 位置的數值 Num ,加上第二行 [1, x] 中前面的整個 k 塊中的 Num 個數,後面再求出最後一個塊中前面到 x 的一段中有幾個 Num,注意這裏不能每一個位置都 O(sqrt n) 求,而是 O(sqrt n) 掃一遍,同時用一個 Cnt[MaxNum] 的數組將掃到的數字對應的累加器+1,這樣掃到一個位置就能夠當即算出第二行這個位置的值了,最後再掃一遍將累加器減回去。class

對於修改第一行的某個位置,顯然能夠向後掃每一個塊而後更新一下 A[][] 數組,然而 B[][] 的維護其實也是能夠枚舉後面的每一個塊而後整體 O(sqrt n) 維護的。test

將修改操做分爲插入和刪除操做就能夠很清晰地維護了。

 

代碼

#include <iostream>
#include <cstdlib>
#include <cstring>
#include <cstdio>
#include <cmath>
#include <algorithm>
#include <map>

using namespace std;

inline int gmax(int a, int b) {return a > b ? a : b;}

inline void Read(int &Num)
{
	char c = getchar(); 
	while (c < '0' || c > '9') c = getchar();
	Num = c - '0'; c = getchar();
	while (c >= '0' && c <= '9')
	{
		Num = Num * 10 + c - '0';
		c = getchar();
	}
}

map<int, int> M;

const int MaxN = 100000 + 5, MaxNum = 200000 + 5, MaxB = 150 + 5;

int n, m, k, Index, Blk, Tot;
int A[MaxN], T[MaxN], Belong[MaxN], L[MaxB], R[MaxB], Sum[MaxB][MaxNum][2], Cnt[MaxNum];

int Query2(int x)
{
	int ret = Sum[Belong[x] - 1][A[x]][0];
	for (int i = L[Belong[x]]; i <= x; ++i)
		if (A[i] == A[x]) ++ret;
	return ret;
}

int Query3(int x)
{
	int Now, Num, ret;
	Num = Query2(x);
	ret = Sum[Belong[x] - 1][Num][1];
	for (int i = L[Belong[x]]; i <= x; ++i)
	{
		++Cnt[A[i]];
		Now = Sum[Belong[x] - 1][A[i]][0] + Cnt[A[i]];
		if (Now == Num) ++ret;
	}
	for (int i = L[Belong[x]]; i <= x; ++i)
		--Cnt[A[i]];
	return ret;
}

int main()
{
	scanf("%d%d%d", &n, &m, &k);
	Index = 0;
	int Num;
	for (int i = 1; i <= m; ++i) 
	{
		Read(Num);
		if (M[Num] == 0) M[Num] = ++Index;
		A[i] = M[Num];
		T[i] = Num;
	}
	Blk = gmax((int)sqrt((double)m), m / 150);
	for (int i = 1; i <= m; ++i)
	{
		Belong[i] = (i - 1) / Blk + 1;
		if (L[Belong[i]] == 0) L[Belong[i]] = i;
		R[Belong[i]] = i;
	}
	Tot = Belong[m];
	for (int i = 1; i <= m; ++i)
		for (int j = Belong[i]; j <= Tot; ++j)
			++Sum[j][A[i]][0];
	for (int i = 1; i <= m; ++i)
	{
		Num = Query2(i);
		for (int j = Belong[i]; j <= Tot; ++j)
			++Sum[j][Num][1];
	}
	int t, x, y, Ans;
	for (int i = 1; i <= k; ++i)
	{
		Read(t); Read(x); Read(y);
		if (t == 0)
		{
			if (x == 1) Ans = T[y];
			else if (x & 1) Ans = Query3(y);
			else Ans = Query2(y);
			printf("%d\n", Ans);
		}
		else
		{
			T[x] = y;
			if (M[y] == 0) M[y] = ++Index;
			y = M[y];
			for (int j = Belong[x]; j <= Tot; ++j)
				--Sum[j][Sum[j][A[x]][0]][1];
			for (int j = Belong[x]; j <= Tot; ++j)
				--Sum[j][A[x]][0];	
			A[x] = y;
			for (int j = Belong[x]; j <= Tot; ++j)
				++Sum[j][A[x]][0];
			for (int j = Belong[x]; j <= Tot; ++j)
				++Sum[j][Sum[j][A[x]][0]][1];
		}
	}
	return 0;
}
相關文章
相關標籤/搜索