牛客網-看花 https://www.nowcoder.com/test/question/1d6f8e0e16de49d094d16057c92d49de?pid=17906015&tid=28876854node
小明有一個花園,花園裏面一共有m朵花,對於每一朵花,都是不同的,小明用1~m中的一個整數表示每一朵花。ios
他很喜歡去看這些花,有一天他看了n次,並將n次他看花的種類是什麼按照時間順序記錄下來。數組
記錄用a[i]表示,表示第i次他看了a[i]這朵花。spa
小紅很好奇,她有Q個問題,問[l,r]的時間內,小明一共看了多少朵不一樣的花兒,小明由於在忙着欣賞他的花兒,因此想請你幫他回答這些問題。.net
輸入兩個數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]的時間內看了多少種花。
5 3 1 2 3 2 2 3 1 4 2 4 1 5
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/47054947code
#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\n",ans[i]); return 0; }