1. 程式人生 > >查詢區間內等於x的數的個數(分塊)

查詢區間內等於x的數的個數(分塊)

問題:有一個有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;
}