题目连接: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; }