可以发现,这道题目在线很难做(没准真的有某些神犇做得出来呢。。),于是(本蒟蒻)只能采用离线的做法,具体做法是这样的:首先,将所有的询问按右端点排个序,左端点不管,然后按顺序做每一个询问,那具体怎么做呢,见下:
比如当前我们做到某个询问,它的左端点为l,右端点为r,那么我们需要将项链中前r个贝壳给处理完,因为r是升序的,所以这个处理其实只需要线性时间,那么当前我们应该处理1~r区间内的贝壳,先定义一个s[i],用法后面会提到,那么怎么处理呢?对于每一种贝壳,我们使所有这种贝壳中出现最晚的——也就是最右边的——那个的s值变成1,其他的都为0,处理完之后,我们就可以直接统计l~r中的s值的和即可,这样子可以保证每种贝壳只会被算一个,那么区间和这个东西我们又可以用树状数组来维护,这样时间复杂度就会很优了,并且其实s数组是不用出现的。那么接下来......上代码!
#include <cstdio> #include <cstring> #include <algorithm> using namespace std; int n,m,a[500010],last[1000010];//last记录每种颜色最后出现的那个的位置 struct node{int x,y,z;}; node b[200010]; int tree[500010];//树状数组 int lowbit(int x){return ((-x)&x);} bool cmp(node x,node y){return x.y<y.y;}//以右端点为关键字 void change(int x,int y)//修改 { if(x==0)return; for(int i=x;i<=n;i+=lowbit(i)) tree[i]+=y; } int sum(int x)//查询 { int p=0; for(int i=x;i>=1;i-=lowbit(i)) p+=tree[i]; return p; } int ans[200010]; int main() { scanf("%d",&n); for(int i=1;i<=n;i++) scanf("%d",&a[i]); memset(last,0,sizeof(last)); scanf("%d",&m); for(int i=1;i<=m;i++) scanf("%d %d",&b[i].x,&b[i].y),b[i].z=i;//b[i].z用来记录询问的编号,便于输出 sort(b+1,b+m+1,cmp);//排序 int j=0;//记录处理到了哪个贝壳 memset(tree,0,sizeof(tree));// for(int i=1;i<=m;i++) { while(j<b[i].y)// { j++; change(last[a[j]],-1);//将前面的一个的标记去掉 last[a[j]]=j;//记录 change(j,1);//重新标记 } ans[b[i].z]=sum(b[i].y)-sum(b[i].x-1);//统计区间和并记录 } for(int i=1;i<=m;i++) printf("%d\n",ans[i]); }