一个比较裸的题目吧。
题目大意:
给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; }