查詢區間內等於x的數的個數(分塊)
阿新 • • 發佈:2019-01-31
問題:有一個有n個數的陣列,q次查詢,每次查詢l, r ,x,查詢區間[l,r]內等於x的數的個數
思路:分塊。
就把這題當成是分塊的入門題來講解一下分塊。分塊其實就是一種比較優美的暴力(我覺得),一般的分塊都是把長度為n的陣列分成每一塊為sqrt(n)個數的多個塊。然後對於區間的操作就可以不再是一個一個數進行處理,而是可以一個塊一個塊的處理,每次查詢最多會涉及到sqrt(n)個塊,這樣複雜度就降了下來。
(先進行幾點說明,本文中的陣列下標從1開始,塊的編號也是從1開始,每個塊的大小k = sqrt(n))
分塊過後的對於區間[l,r]的操作就分為兩種情況:
設cl為區間左端點l所在的塊號,cr為區間右端點r所在的塊號。那麼怎麼求一個下標所屬的塊號呢,很簡單:(x-1)/k + 1。
第一種情況是cl == cr,也就是左右端點在同一個塊內,這樣直接從l迴圈到r處理操作就可以了,複雜度很顯然最多是sqrt(n)
第二種情況我們可以統一的假定為 cl != cr,這樣我們可以把區間分為三部分,一是l到cl*k這部分可以直接迴圈操作,後面就是第cl+1塊到cr-1塊這部分,最後就是(cr-1)*k+1到r這部分。
這一題要求的是一個區間內等於x的有多少個數,我們可以對每一個塊進行排序,然後就可以用二分求出等於x的個數。
#include<cmath> #include<cstdio> #include<cstring> #include<iostream> #include<algorithm> using namespace std; const int maxn = 300010; int a[maxn]; int b[maxn]; int n,k; int li(int x) //求x的塊號 { return (x-1)/k + 1; } int query(int l,int r,int x) { int i; int cl = li(l); int cr = li(r); int cnt = 0; if(cl == cr) { for(i=l;i<=r;i++) if(a[i] == x) cnt++; return cnt; } for(i=l;i<=cl*k;i++) { if(a[i] == x) cnt++; } for(i=cl+1;i<cr;i++) { cnt += upper_bound(b+(i-1)*k+1,b+i*k+1,x) - lower_bound(b+(i-1)*k+1,b+i*k+1,x); } for(i=(cr-1)*k+1;i<=r;i++) { if(a[i] == x) cnt++; } return cnt; } int main(void) { int i,j; scanf("%d",&n); for(i=1;i<=n;i++) { scanf("%d",&a[i]); b[i] = a[i]; } k = sqrt(n); for(i=1;i<=k+1;i++) { if((i-1)*k+1 > n) break; sort(b+(i-1)*k+1,b+min(i*k,n)+1); } int q; scanf("%d",&q); while(q--) { int l,r,x; scanf("%d%d%d",&l,&r,&x); int ans = query(l,r,x); printf("%d\n",ans); } return 0; }