人生第一次接触分块。以前也听过分块的思想,不过实践这还是第一次,调了老半天。
很显然我就是用分块思想写的这道题。不过貌似正解是树状数组,但是鉴于分块比较好像,就写的分块。毕竟第一次写分块,先谈谈自己对分块的理解。
刘汝佳叔叔貌似把分块算作数据结构。不过以我目前的理解程度,觉得分块更像是一种思想,一种优化暴力的思想。与分治不同,分治是把整个问题划分成小问题解决;而分块是把原问题划分成很多整块,对于每个块暴力求解,再求解块与块之间联系着的解,对于一次询问,在一段连续的整块内可以O(1)得出,而不满一块的两段部分暴力求解。分块做法就是把O(1)得到的那部分“整”的预处理出来,从原本的暴力优化成了O(1),“零”的暴力解。
分块做法大多数把原问题分成ceil(√n)块,每块大小是floor(√n),这样做貌似预期复杂度是最小的,这不是分块思想的重点。
对于这道题,我们把区间分为ceil(√n)块,首先对于每一块暴力求出不同数字的个数复杂度O(n)。设f[i][j]表示第i块到第j块不同数字的个数,我们要由f[i][i]求出f[i][j],还需要一些量:我们要知道第i个数左边第一个和它相同的数的位置l[i]以及右边的第一个r[i]。那么我们就可以求出f数组,具体做法很灵活,比如:f[i][j] = f[i+1][j],再在块i内寻找i+1~j块中没出现过的,加到f[i][j]中即可,这就用到了r数组。
预处理完毕后就可以读入询问了,对于一个询问[a,b],我们先求出a,b所在的块fa,fb,连续的整块fa+1~fb-1可以O(1)得到就是f[fa+1][fb-1],之后暴力求出a所在块以及b所在块内出现的加到res中即可,需要用到之前求出的l以及r数组。这个过程要不重不漏,需要注意细节。
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstring>
using namespace std;
int n, m, num, siz;
int w[50005], f[405][405];
int l[50005], r[50005], last[1000005];
bool vis[1000005];
void get(int &x){
x = 0; char c = getchar();
while(c < '0' || c > '9') c = getchar();
while(c <= '9' && c >= '0') x = x*10+c-48, c = getchar();
}
int main()
{
get(n);
num = ceil(sqrt(n));
siz = floor(sqrt(n));
memset(r, 0x3f, sizeof r);
for(int i = 1; i <= n; i++){
get(w[i]);
l[i] = last[w[i]];
last[w[i]] = i;
r[l[i]] = i;
}
for(int i = 1; i <= num; i++){
memset(vis, 0, sizeof vis);
int beg = (i-1)*siz+1;
int end = min(i*siz, n);
for(int j = beg; j <= end; j++){
f[i][i] += !vis[w[j]];
vis[w[j]] = 1;
}
}
for(int i = 1; i < num; i++)
for(int j = 1; i+j <= num; j++){
f[j][i+j] = f[j+1][i+j];
int beg = (j-1)*siz + 1;
int end = j * siz;
int border = (i+j)*siz;
for(int k = beg; k <= end; k++){
f[j][i+j] += (r[k] > border);
}
}
get(m);
while(m--){
int a, b;
get(a); get(b);
int fa = a/siz+1;
int fb = b/siz+1;
int res = f[fa+1][fb-1];
if(fb-fa <= 1){
memset(vis, 0, sizeof vis);
for(int i = a; i <= b; i++){
res += !vis[w[i]];
vis[w[i]] = 1;
}
printf("%d\n", res);
continue;
}
int rb = (fb-1) * siz;
int enda = fa * siz;
int begb = (fb-1)*siz+1;
for(int i = a; i <= enda; i++){
res += (r[i] > rb);
}
for(int i = begb; i <= b; i++){
res += (l[i] < a);
}
printf("%d\n", res);
}
return 0;
}