bzoj1878 [SDOI2009]HH的项链
1878: [SDOI2009]HH的项链
Time Limit: 4 Sec Memory Limit: 64 MBDescription
HH有一串由各种漂亮的贝壳组成的项链。HH相信不同的贝壳会带来好运,所以每次散步 完后,他都会随意取出一
段贝壳,思考它们所表达的含义。HH不断地收集新的贝壳,因此他的项链变得越来越长。有一天,他突然提出了一
个问题:某一段贝壳中,包含了多少种不同的贝壳?这个问题很难回答。。。因为项链实在是太长了。于是,他只
好求助睿智的你,来解决这个问题。
Input
第一行:一个整数N,表示项链的长度。
第二行:N个整数,表示依次表示项链中贝壳的编号(编号为0到1000000之间的整数)。
第三行:一个整数M,表示HH询问的个数。
接下来M行:每行两个整数,L和R(1 ≤ L ≤ R ≤ N),表示询问的区间。
N ≤ 50000,M ≤ 200000。
Output
M行,每行一个整数,依次表示询问对应的答案。
Sample Input
6
1 2 3 4 3 5
3
1 2
3 5
2 6
1 2 3 4 3 5
3
1 2
3 5
2 6
Sample Output
2
2
4
2
4
HINT
Source
Tips:
这道题你很容易想到用一些线段树,树状数组之类的算法;
但是却很难用上去;
因为它是离线的;
我们可以有序的处理询问,以询问左端点排序;
现将每个数第一次出现的位置赋值为一,维护一棵线段树,并记录每个数的下一个重复的数的位置;
当处理到了第i个询问,那么前l[i]个数已经不需要了,将前l[i]个数上的一,转移到next[i]上,做一遍线段树的询问;
Code:
#include<iostream> #include<algorithm> #include<cstdio> #include<cstring> #define MAXN 100008 using namespace std; int n,m,tree[2*MAXN],a[MAXN],next[MAXN],boo[MAXN*10],pre[MAXN]; int ans[2*MAXN]; struct node{ int x,y,pos; }f[MAXN*2]; int query(int l,int r,int li,int ri,int v){ if(l==li&&r==ri){ return tree[v]; } int mid=(l+r) >> 1; if(ri<=mid){ return query(l,mid,li,ri,v<<1); } if(li>mid){ return query(mid+1,r,li,ri,(v<<1)+1); } return query(l,mid,li,mid,v<<1)+query(mid+1,r,mid+1,ri,(v<<1)+1); } void add(int l,int r,int flag,int v){ if(l==r){ tree[v]=1; return; } int mid=(l+r) >> 1; if(flag<=mid) add(l,mid,flag,v<<1); else add(mid+1,r,flag,(v<<1)+1); tree[v]=tree[v<<1]+tree[(v<<1)+1]; } bool cmp(node aa,node bb){ return aa.x<bb.x; } int main(){ scanf("%d",&n); for(int i=1;i<=n;i++){ scanf("%d",&a[i]); if(!boo[a[i]]){ add(1,n,i,1); boo[a[i]]=i; }else{ pre[i]=boo[a[i]]; boo[a[i]]=i; next[pre[i]]=i; } } scanf("%d",&m); for(int i=1;i<=m;i++){ scanf("%d%d",&f[i].x,&f[i].y); f[i].pos=i; } f[0].x=1; sort(f+1,f+m+1,cmp); for(int j=1;j<=m;j++){ for(int i=f[j-1].x;i<f[j].x;i++){ add(1,n,next[i],1); } ans[f[j].pos]=query(1,n,f[j].x,f[j].y,1); } for(int i=1;i<=m;i++) printf("%d\n",ans[i]); }