题目链接:http://www.lydsy.com/JudgeOnline/problem.php?id=1878
题意:给定一段区间,长度问N,查询某一区间内数值不同的数的个数,共有M个查询。
数据范围:N ≤ 50000,M ≤ 200000。
这个题的解法并非对每个答案直接查询,而是对询问排序后回答。
记一个数组B,起初,对于每个颜色第一次出现的i,B[i]=1。
这时,对于左端点是1的询问(1,j),答案即为sum[B[1]..B[j]]。
当处理完以i为左端点的讯问后,B[i]=B[i]-1,对i上颜色下一次出现的地方j,B[j]=B[j]+1,
这样左端点是i+1的询问,答案即为sum[B[i+1]..B[j]]。
#include <iostream> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <math.h> #include <map> #include <queue> #include <algorithm> using namespace std; #define Maxn 500005 #define Maxm 200005 #define MaxnRange 1000005 #define lx (x<<1) #define rx ((x<<1) | 1) #define MID ((l + r)>>1) int next[Maxn]; int ans[Maxn]; int n,m; int A[Maxn]; int times[Maxn]; int before[MaxnRange]; struct Que { int l,r; int k; int val; bool operator <(const Que &a) const { return l<a.l || (l == a.l && r<a.r) || (l == a.l && r ==a.r && k<a.k ); } }que[Maxm]; //线段树数组 int S[Maxn<<2]; //向上更新 void pushUp(int x) { S[x] = S[lx] + S[rx]; } //区间查询 int query(int L,int R,int l,int r,int x) { if(L<=l && r<=R) return S[x]; int ans = 0; if(L<=MID) ans += query(L,R,l,MID,lx); if(MID<R) ans += query(L,R,MID+1,r,rx); return ans; } //单点更新 void update(int p,int d,int l,int r,int x) { if(l == r) { S[x] += d; return; } if(p<=MID) update(p,d,l,MID,lx); else update(p,d,MID+1,r,rx); pushUp(x); } int main() { #ifndef ONLINE_JUDGE freopen("in.txt","r",stdin); #endif int a; scanf(" %d",&n); for(int i=1;i<=n;i++) { scanf(" %d",&a); if(!before[a]) update(i,1,1,n,1); else next[before[a]] = i; before[a] = i; } scanf(" %d",&m); for(int i=1;i<=m;i++) { scanf(" %d %d",&que[i].l,&que[i].r); que[i].k = i; } sort(que+1,que+1+m); //必须从n开始而不是m int j = 1; for(int i=1;i<=n;i++) { while(j<=m && que[j].l == i) { ans[que[j].k] = query(que[j].l,que[j].r,1,n,1); j++; } update(i,-1,1,n,1); if(next[i]) update(next[i],1,1,n,1); } for(int i=1;i<=m;i++) { printf("%d\n",ans[i]); } return 0; }