题解:P1972 [SDOI2009] HH的项链

· · 题解

大家好,我是个毒瘤,我非常喜欢暴力数据结构,于是我就用分块过了这个题。

思路

对于这道题,我们把区间分 \sqrt n 块。

我们首先对于每一块暴力求出不同数字的个数,时间复杂度复杂度 O(n)

f_{i,j} 表示第 i 块到第 j 块不同数字的个数。

如果我们要由 f_{i,i} 求出 f_{i,j},那么我们还需要一些量:我们要知道第 i 个数左边第一个和它相同的数的位置 l_i 以及右边的第一个 r_i。那么我们就可以求出 f 数组。

预处理完毕后就可以读入询问了,对于一个询问 [a,b],我们先求出 [a,b] 所在的块 fa,fb,连续的整块 fa+1,到 fb-1 可以 O(1) 得到就是 f_{fa+1,fb-1},之后暴力求出 a 所在块以及 b 所在块内出现的加到 res 中即可,需要用到之前求出的 l 以及 r 数组。

实现

对于 f 数组我们可以这么求:

## 代码 ```cpp #include<bits/stdc++.h> using namespace std; inline int read(){ int s=0,w=1; char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-')w=-1;ch=getchar();} while(ch>='0'&&ch<='9') s=s*10+ch-'0',ch=getchar(); return s*w; } inline void write(int x){ if(x==0){putchar('0');return;} int len=0,k1=x,c[10005]; if(k1<0)k1=-k1,putchar('-'); while(k1)c[len++]=k1%10+'0',k1/=10; while(len--)putchar(c[len]); } const int N=1e6+5,M=1e3+5; int w[N],f[M][M],l[N],r[N],last[N]; bitset<N>vis; int main(){ int n=read(),num=ceil(sqrt(n)),siz=floor(sqrt(n)); memset(r,0x3f,sizeof r); for(int i=1;i<=n;i++){ w[i]=read(); l[i]=last[w[i]]; last[w[i]]=i; r[l[i]]=i; } for(int i=1;i<=num;i++){ vis.reset(); int beg=(i-1)*siz+1,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,end=j*siz; int border=(i+j)*siz; for(int k=beg;k<=end;k++){ f[j][i+j]+=(r[k]>border); } } } int m=read(); while(m--){ int a=read(),b=read(); int fa=a/siz+1,fb=b/siz+1; int res=f[fa+1][fb-1]; if(fb-fa<=1){ vis.reset(); for(int i=a;i<=b;i++){ res+=!vis[w[i]]; vis[w[i]]=1; } write(res),puts(""); continue; } int rb=(fb-1)*siz; int enda=fa*siz,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); write(res),puts(""); } return 0; } ```