[bzoj1878] [SDOI2009]HH的项链(树状数组+离线)
1878: [SDOI2009]HH的项链
Time Limit: 4 Sec Memory Limit: 64 MBSubmit: 3210 Solved: 1619
[Submit][Status][Discuss]
Description
HH有一串由各种漂亮的贝壳组成的项链。HH相信不同的贝壳会带来好运,所以每次散步 完后,他都会随意取出一段贝壳,思考它们所表达的含义。HH不断地收集新的贝壳,因此, 他的项链变得越来越长。有一天,他突然提出了一个问题:某一段贝壳中,包含了多少种不同 的贝壳?这个问题很难回答。。。因为项链实在是太长了。于是,他只好求助睿智的你,来解 决这个问题。
Input
第一行:一个整数N,表示项链的长度。 第二行:N个整数,表示依次表示项链中贝壳的编号(编号为0到1000000之间的整数)。 第三行:一个整数M,表示HH询问的个数。 接下来M行:每行两个整数,L和R(1 ≤ L ≤ R ≤ N),表示询问的区间。
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
对于20%的数据,N ≤ 100,M ≤ 1000;
对于40%的数据,N ≤ 3000,M ≤ 200000;
对于100%的数据,N ≤ 50000,M ≤ 200000。
Source
头一次离线做这种题
一个显然错误的做法:直接算有几个贝壳,sb地用树状数组求01区间的前缀和
但这什么时候正确呢?
当且仅当询问的区间一种颜色只出现了一次
那么我们不妨先读进来要求的区间,按左端点排序
一个一个处理询问,这样相当于在模拟一个类似滑动窗口的东西,
当滑动窗口左边扫到一种颜色时,即这种颜色不再计算时,我们再添加下一个和它颜色相同的元素
因为同色元素位置关系是唯一的,所以在给答案加一时,询问窗口内仅存在一个一种颜色的元素
处理下一个颜色可以预处理出来,初始化时要添加每种颜色的第一个元素
#include<stdio.h> #include<stdlib.h> #include<string.h> #include<algorithm> #define LL long long using namespace std; typedef struct{ int l,r; int ans; int pos; }qry; qry qr[200010]; int a[50050]; int now[1001000],next[50050]; int bit[50050],n; int cmp1(const qry a,const qry b){ if(a.l<b.l)return 1; else if(a.l==b.l)return a.r<b.r; else return 0; } int cmp2(const qry a,const qry b){ return a.pos<b.pos; } int lb(int x){ return x&(-x); } int c(int x){ while(x<=n){ bit[x]++; x+=lb(x); } return 0; } int q(int x){ int ans=0; while(x){ ans+=bit[x]; x-=lb(x); } return ans; } int main(){ scanf("%d",&n); for(int i=1;i<=n;i++){ scanf("%d",&a[i]); a[i]++;//0 if(!now[a[i]])c(i); next[now[a[i]]]=i; now[a[i]]=i; } int m; scanf("%d",&m); for(int i=1;i<=m;i++){ scanf("%d %d",&qr[i].l,&qr[i].r); qr[i].pos=i; } sort(qr+1,qr+m+1,cmp1); int l=1; for(int i=1;i<=m;i++){ //????? while(l<qr[i].l){ if(next[l])c(next[l]); l++; } qr[i].ans=q(qr[i].r)-q(qr[i].l-1); } sort(qr+1,qr+m+1,cmp2); for(int i=1;i<=m;i++)printf("%d\n",qr[i].ans); return 0; }