1878: [SDOI2009]HH的项链
题目描述
HH有一串由各种漂亮的贝壳组成的项链。HH相信不同的贝壳会带来好运,所以每次散步 完后,他都会随意取出一段贝壳,思考它们所表达的含义。HH不断地收集新的贝壳,因此他的项链变得越来越长。有一天,他突然提出了一个问题:某一段贝壳中,包含了多少种不同的贝壳?这个问题很难回答。。。因为项链实在是太长了。于是,他只好求助睿智的你,来解决这个问题。
输入
第一行:一个整数N,表示项链的长度。
第二行:N个整数,表示依次表示项链中贝壳的编号(编号为0到1000000之间的整数)。
第三行:一个整数M,表示HH询问的个数。
接下来M行:每行两个整数,L和R(1 ≤ L ≤ R ≤ N),表示询问的区间。N ≤ 50000,M ≤ 200000。
输出
M行,每行一个整数,依次表示询问对应的答案。
解题思路
方法一
我想各位dalao一看到这种多次查询一个区间的题目就会马上想到莫队算法,没错这题可以用莫队水过,下面附上弱(chu)弱(nv)的(zuo)代码。
#include<cstdio>
#include<cmath>
#include<algorithm>
using namespace std;
const int maxn=50005,maxm=200005,maxv=1000005;
struct jz{
int id,L,R;
}q[maxm];
int h[maxn],n,m,a[maxn],sum[maxv],ans[maxm],tot;
bool cmp(jz a,jz b){
if (h[a.L]==h[b.L]) return a.R<b.R;
return a.L<b.L;
}
inline int _read(){
int num=0;char ch=getchar();
while (ch<'0'||ch>'9') ch=getchar();
while (ch>='0'&&ch<='9') num=num*10+ch-48,ch=getchar();
return num;
}
void move(int x,int y){
if (y==1&&sum[a[x]]==0) tot++;
if (y==-1&&sum[a[x]]==1) tot--;
sum[a[x]]+=y;
}
int main(){
freopen("exam.in","r",stdin);
freopen("exam.out","w",stdout);
n=_read();for (int i=1;i<=n;i++) scanf("%d",&a[i]);
m=_read();for (int i=1;i<=m;i++) scanf("%d%d",&q[i].L,&q[i].R),q[i].id=i;
int k=sqrt(n);for (int i=1;i<=n;i++) h[i]=(i-1)/k+1;
sort(q+1,q+1+m,cmp);
int L=1,R=1;move(1,1);
for (int i=1;i<=m;i++){
while (L>q[i].L) move(--L,1);
while (L<q[i].L) move(L++,-1);
while (R<q[i].R) move(++R,1);
while (R>q[i].R) move(R--,-1);
ans[q[i].id]=tot;
}
for (int i=1;i<=m;i++) printf("%d\n",ans[i]);
return 0;
}
方法二
还有一种比较巧妙的方法就是离线所有询问,使起点递增,用k表示当前起点,显然处在[k,n]每种贝壳的第一个才是值得我们关注的,将这些点标为1,其余为0,种类数就是到k的前缀和(用树状数组能方便求出)。如果起点需要移动,就把nxt[a[i]]标为1(nxt数组可以提前构造)。
#include<cstdio>
#include<algorithm>
using namespace std;
const int maxn=50005,maxm=1000005;
int ans[maxn*4],a[maxn],n,m,nxt[maxn],w[maxm],x[maxn],mx;
struct jz{
int x,y,w;
bool operator<(const jz&b)const{
return x<b.x;
}
}b[maxn*4];
inline int _read(){
int num=0;char ch=getchar();
while (ch<'0'||ch>'9') ch=getchar();
while (ch>='0'&&ch<='9') num=num*10+ch-48,ch=getchar();
return num;
}
int lowbit(int x){return x&(-x);}
void change(int x,int y){
while (x<=n){
a[x]+=y;
x+=lowbit(x);
}
}
int ask(int x){
int num=0;
while (x>0){
num+=a[x];
x-=lowbit(x);
}
return num;
}
int main(){
freopen("exam.in","r",stdin);
freopen("exam.out","w",stdout);
n=_read();
for (int i=1;i<=n;i++) x[i]=_read();
for (int i=n;i>=1;i--){
if (w[x[i]]!=0) nxt[i]=w[x[i]];
w[x[i]]=i;mx=max(x[i],mx);
}
m=_read();
for (int i=1;i<=m;i++) b[i].x=_read(),b[i].y=_read(),b[i].w=i;
sort(b+1,b+1+m);
for (int i=1;i<=mx;i++) if (w[i]!=0) change(w[i],1);b[0].x=1;
for (int i=1;i<=m;i++){
for (int j=b[i-1].x;j<=b[i].x-1;j++) if (nxt[j]!=0) change(nxt[j],1),change(j,-1);
ans[b[i].w]=ask(b[i].y)-ask(b[i].x-1);
}
for (int i=1;i<=m;i++) printf("%d\n",ans[i]);
return 0;
}