一个比较裸的题目吧。

题目大意:
给n个数m次询问。每次询问一个区间[l,r]和一个数k,问区间[l,r]中小于等于k的数字个数。
n,m<=10^5,a[i]<=10^5

思路:
直接暴力查找肯定是不可行的。直接开桶的话,空间上也不允许。
但是我们可以分块。
把n个数分成n/sqrt(n)个块,每个块里sqrt(n)个数。最后一个块可能不足sqrt(n)个。
然后对每个块里的数字进行统计。由于n<=10^5,最多分出sqrt(10^5)=320个块左右。
那么我们可以开一个数组c[320][100040]表示第i个块中数字j出现了多少次,从时间和空间上都是开的下的。
统计完以后我们可以再for一遍把c数组变成一个前缀和数组,处理完以后我们的c[i][j]就变成了第i个块中小于等于数字j的数字个数。
剩下的就是直接查询了,由于我们已经预处理出前缀和数组。那么我们可以直接在线解决每一次的查询问题。计算出l和r所在的块,然后中间的直接按块跳过去,两个边界部分我们可以暴力计算。
这样做的复杂度就是m3sqrt(n),跑了500ms。

代码:

#include<bits/stdc++.h>
using namespace std;
int n,m;
int block;
int a[100040],c[330][100020];
int main()
{
    cin>>n>>m;
    for(int i=1;i<=n;i++)cin>>a[i];
    block=n/sqrt(n);
    int t=sqrt(n);
    for(int i=1;i<=n;i++){
        int b=(i-1)/t;
        c[b][a[i]]++;
    }
    for(int i=0;i<block;i++){
        for(int j=1;j<=100000;j++){
            c[i][j]+=c[i][j-1];
        }
    }
    while(m--){
        int l,r,k;
        cin>>l>>r>>k;
        int b1,b2;
        b1=(l-1)/t;
        b2=(r-1)/t;
        int tot=0;
        if(b1==b2){
            for(int i=l;i<=r;i++){
                if(a[i]<=k)tot++;
            }
        }
        else {
            for(int i=l;i<=(b1+1)*t;i++){
                if(a[i]<=k)tot++;
            }
            if(b1<b2){
                for(int i=b1+1;i<b2;i++){
                    tot+=c[i][k];
                }
            }
            for(int i=b2*t+1;i<=r;i++){
                if(a[i]<=k)tot++;
            }
        }

        cout<<tot<<endl;
    }
    return 0;
}

题目叫做“换个角度思考”,除了在线解决,要换个角度的话,就是离线了。
再来看看离线的方法, 本题显然是可以离线的。看到题目的时候感觉可能可以用莫队来做,不过我不会莫队
还是用树状数组吧!简单易懂。
对于[l,r]的区间查询问题,我们可以转化成求[1,r]-[1,l-1]的问题。而树状数组可以在logn时间内解决这个问题。但是我们其实希望在查询[l,r]的时候,[1,r]-[1,l-1]里的结果是满足小于等于k的数。
因此我最好可以每次对[l,r]的查询都可以满足里面的数都是小于等于k的。
那么我们可以先对查询进行排序,按照k从小到大排序。然后对a[i]进行从小到大排序。
然后我们对排序后的查询重新进行处理,由于现在的查询k是从小到大的,那么我们每次查询之前,我们都从a[i]数组中把小于等于k的数字放入树状数组中。然后再去查询。

上述的思路方法就是离线处理。问题就解决了,这样做复杂度是m*logn。

代码:

#include<bits/stdc++.h>
using namespace std;
int n,m;
int tree[100040],ans[100040];
int lowbit(int x){
    return x&(-x);
}
void update(int x){
    while(x<=n){
        tree[x]+=1;
        x+=lowbit(x);
    }
}
int getans(int x){
    int ans=0;
    while(x){
        ans+=tree[x];
        x-=lowbit(x);
    }
    return ans;
}
pair<int,int>p[100040];
struct node{
    int l,r,k,idx;
}q[100005];
bool cmp(node a,node b){
    return a.k<b.k;
}
bool cmp1(pair<int,int>a,pair<int,int>b){
    return a.first<b.first;
}
int main()
{
    cin>>n>>m;
    for(int i=1;i<=n;i++){
        int x;
        cin>>x;
        p[i]=make_pair(x,i);
    }
    sort(p+1,p+1+n,cmp1);
    for(int i=1;i<=m;i++){
        cin>>q[i].l>>q[i].r>>q[i].k;
        q[i].idx=i;
    }
    sort(q+1,q+1+m,cmp);

    int j=1;
    for(int i=1;i<=m;i++){
        for(;j<=n;j++){
            if(p[j].first<=q[i].k)update(p[j].second);
            else break;
        }
        ans[q[i].idx]=getans(q[i].r)-getans(q[i].l-1);
    }
    for(int i=1;i<=m;i++){
        cout<<ans[i]<<endl;
    }
    return 0;
}