牛客网-看花 https://www.nowcoder.com/test/question/1d6f8e0e16de49d094d16057c92d49de?pid=17906015&tid=28876854
小明有一个花园,花园里面一共有m朵花,对于每一朵花,都是不一样的,小明用1~m中的一个整数表示每一朵花。
他很喜欢去看这些花,有一天他看了n次,并将n次他看花的种类是什么按照时间顺序记录下来。
记录用a[i]表示,表示第i次他看了a[i]这朵花。
小红很好奇,她有Q个问题,问[l,r]的时间内,小明一共看了多少朵不同的花儿,小明因为在忙着欣赏他的花儿,所以想请你帮他回答这些问题。
输入描述:
输入两个数n,m;(1<=n<=2000,1<=m<=100);分别表示n次看花,m表示一共有m朵花儿。
接下来输入n个数a[1]~a[n],a[i]表示第i次,小明看的花的种类;
输入一个数Q(1<=Q<=1000000);表示小红的问题数量。
输入Q行 每行两个数l,r(1<=l<=r<=n);表示小红想知道在第l次到第r次,小明一共看了多少不同的花儿。
输出描述:
一共Q行
每一行输出一个数 表示小明在[l,r]的时间内看了多少种花。
输入例子1:
5 3 1 2 3 2 2 3 1 4 2 4 1 5
输出例子1:
3 2 3
离线查询的思想。预处理:先将N个数字读取进来,用next[]数组储存每个数字下一次出现的位置,fir[i]布尔数组表示从当前查询的位置到结尾第i个元素是否为第一次出现,那么区间[1,x]的fir[i]的和即为这个区间的数的个数,用一个树状数组即可。再将M次查询读取进来,按查询区间的左端点进行排序,然后开始,遍历M个查询的左端点,将区间以前出现过的数字用next[]数组转移至左端点以后,这样区间[左端点的坐标,x]的fir[i]的和即为这个区间的数的个数。再对这个区间进行查询。时间复杂度:预处理O(N + Mlog(M)) 查询O(Mlog(N)+N)
原文链接:https://blog.csdn.net/CZWin32768/article/details/47054947
#include<iostream> #include<algorithm> #include<math.h> #define ll long long #define M 0x3f3f3f3f3f using namespace std; int n,m,q; int a[50005],c[50005],ans[2000005],Next[50005],vis[1000005]; bool is_first[50005]; //a[]输入的n个数 //c[]树状数组求和 //ans[i]第i个查询的结果 //is_first[]判断第i个数是否出现过 //vis[]记录第i个数出现的位置 //Next[i]第i个数下一次要出现的位置 struct node { int le; int ri; int pos; }p[4000005]; bool cmp(node x,node y)//按左端点从小到大排序 { return x.le<y.le; } int lowbit(int x) { return x&(-x); } void update(int x,int v)//更新is_first[x]的值,同时更新相应和 { while(x<=n) { c[x]=c[x]+v; x=x+lowbit(x); } } int getsum(int x) { int sum=0; while(x>0) { sum=sum+c[x]; x=x-lowbit(x); } return sum; } int main() { scanf("%d%d",&n,&m); for(int i=1;i<=n;i++) scanf("%d",&a[i]); for(int i=n;i>=1;i--)//预处理第i个数出现的位置 { if(!vis[a[i]]) { vis[a[i]]=i;//标记出现的位置 is_first[i]=true; } else { Next[i]=vis[a[i]];//数字a[i]下一次要出现的位置 is_first[Next[i]]=false; is_first[i]=true;//更新状态 vis[a[i]]=i; } } scanf("%d",&q); for(int i=1;i<=q;i++) { scanf("%d%d",&p[i].le,&p[i].ri); p[i].pos=i; } sort(p+1,p+q+1,cmp); for(int i=1;i<=n;i++) { if(is_first[i])//a[i]是第一次出现,维护树状数组(初始化) update(i,1); } int now=p[1].le; int k=1; for(int i=1;i<=q;i++) { for(;k<p[i].le;k++)//在[1,le)区间出现过的数置-1,再将这些数转移到在区间[len,n]出现的位置并置1 { if(is_first[k]) { update(k,-1); is_first[k]=false; if(Next[k]) { is_first[Next[k]]=true; update(Next[k],1); } } } k=p[i].le; now=p[i].le; ans[p[i].pos]=getsum(p[i].ri)-getsum(p[i].le-1); } for(int i=1;i<=q;i++) printf("%d ",ans[i]); return 0; }