题目描述
HH 有一串由各种漂亮的贝壳组成的项链。HH 相信不同的贝壳会带来好运,所以每次散步完后,他都会随意取出一段贝壳,思考它们所表达的含义。HH 不断地收集新的贝壳,因此,他的项链变得越来越长。有一天,他突然提出了一个问题:某一段贝壳中,包含了多少种不同的贝壳?这个问题很难回答……因为项链实在是太长了。于是,他只好求助睿智的你,来解决这个问题。
输入输出格式
输入格式:
第一行:一个整数N,表示项链的长度。
第二行:N 个整数,表示依次表示项链中贝壳的编号(编号为0 到1000000 之间的整数)。
第三行:一个整数M,表示HH 询问的个数。
接下来M 行:每行两个整数,L 和R(1 ≤ L ≤ R ≤ N),表示询问的区间。
输出格式:
M 行,每行一个整数,依次表示询问对应的答案。
输入输出样例
6 1 2 3 4 3 5 3 1 2 3 5 2 6
2 2 4
说明
数据范围:
对于100%的数据,N <= 50000,M <= 200000。
解题思路
在线解法听说可以用主席树?那玩意太高端,本蒟蒻不会……
离线解法主要有两种——一种是树状数组求前缀和的奇技淫巧,另一种是分块莫队。我用的是前一种。
离线,自然先把所有询问存下来,然后对所有询问按照左端点升序排序(右端点排不排无所谓的啦,反正又不影响结果)。
然后记录下每种颜色的下一种相同颜色所在的位置,用next[i]表示。
然后在树状数组中把每种颜色第一次出现的位置标记为1,然后就可以按排好的顺序处理所有询问了。
一个个询问可以看作是一个滑动窗口在项链上滑动,读取窗口内的颜色信息。
左端点的左边那些第一次出现的颜色就不能在区间中表示出来了,这是就要把左端点以左的标记为1颜色全部更新到下一个颜色相同的点上(next[i])。
全定义一个变量l (模仿了黄学长的代码),表示目前已更新到第l个节点,那么每次统计答案之前肯定要把l一路更新到ask[i].l的前一位(没被标记为1的就不用更新了),这是询问的区间和就是答案啦!
----------------------------------------------------分割线-------------------------------------------------------
我来填坑啦!并不会输公式,勉强看看吧
莫队离线处理询问区间(li,ri),如果知道了(li,ri)的答案,那我们就能很快知道(li-1,ri)(li+1,ri)(li,ri-1)(li,ri+1)的答案,那么在已知(l[i],r[i])的答案的情况下,我们需要O(abs(l[i]-l[i+1])+abs(r[i]-r[i+1]))的时间暴力得到(l[i+1],r[i+1])的答案。如何使转移代价最少?一种方法是按曼哈顿最小生成树的dfs序转移,但是难度过高,于是——
对于每个读入的询问(l[i],r[i]),我们设pos[i]=l[i]/sqrt(n),(这就是传说中的分块)以pos[i]为第一关键字,r[i]为第二关键字对所有询问进行排序,依次处理询问,这样能跑得很快。时间复杂度变成了O(n^(3/2))。
证明和代码网上遍地都是,这里就不贴了(逃)。下面的代码思路是上面的。
源代码
#include<cstdio> #include<algorithm> #define lowbit(x) (x)&(-x) int n,m,mx=-1; int a[50010]={0},next[50010]={0}; int p[1000010]={0};//每种颜色出现的位置 int c[50010]={0}; int query(int pos) { int ans=0; while(pos) { ans+=c[pos]; pos-=lowbit(pos); } return ans; } void add(int pos,int x) { while(pos<=n) { c[pos]+=x; pos+=lowbit(pos); } } struct Ask{ int l,r,id,ans; }ask[200010]; bool cmp1(const Ask & a,const Ask & b) { return a.l==b.l?a.r<b.r:a.l<b.l; } bool cmp2(const Ask & a,const Ask & b) { return a.id<b.id; } int main() { scanf("%d",&n); for(int i=1;i<=n;i++) scanf("%d",a+i),mx=std::max(a[i],mx); for(int i=n;i>0;i--) { next[i]=p[a[i]]; p[a[i]]=i; } for(int i=1;i<=mx;i++) if(p[i])add(p[i],1); scanf("%d",&m); for(int i=1,l,r;i<=m;i++) { scanf("%d%d",&l,&r); ask[i]={l,r,i,0}; } std::stable_sort(ask+1,ask+1+m,cmp1); for(int i=1,ll=0;i<=m;i++) { while(ll<ask[i].l) { if(next[ll]) add(next[ll],1); ll++; } ask[i].ans=query(ask[i].r)-query(ask[i].l-1); } std::stable_sort(ask+1,ask+1+m,cmp2); for(int i=1;i<=m;i++) printf("%d\n",ask[i].ans); return 0; }