题目链接:Click here~~
今天弄懂了这道被很多人称为水题的题。
题意:
给你一个数列{An},然后有m次查询,每次查询一段区间 [l,r] 内不大于 h 的值的个数。
解题思路:
稍加分析不难发现,问题的解满足区间减法的性质,即 ans[l,r] = ans[1,r] - ans[1,l-1] 。
于是想到可以用树状数组来存储前缀和。
然后,对数列{An} 和 所有查询的 h 排序,各自给它们一个指针 i 和 j 。 ( i 和 j 初始都指向数组排序后的第一个元素 )
接着,从前向后遍历每个查询(即 j 不断向后移),对于每个 i ,判断 A[i].h 与 Q[j].h 的关系。
1、若 A[i].h > Q[j].h,则从 i 以后的 A[i].h 也不会再对这个查询的解有影响。此时我们可以得到这个查询的解 sum(r) - sum(l-1) 。
2、若 A[i].h <= Q[j].h,则它会且仅会对从 j 以后的每个 Q[j].h 产生影响。于是我们需要在区间中对这个位置加1,然后让 i 向后移动。
#include <stdio.h> #include <string.h> #include <algorithm> using namespace std; #define N 100005 struct TT { int h,id; bool operator < (const TT& s)const { return h < s.h; } }A[N]; struct QQ { int l,r,h,id; bool operator < (const QQ& s)const { return h < s.h; } }Q[N]; int c[N],ans[N],n; int lowbit(int x) { return x & -x; } void add(int loc,int val) { while(loc <= n) { c[loc] += val; loc += lowbit(loc); } } int sum(int loc) { int ans = 0; while(loc > 0) { ans += c[loc]; loc -= lowbit(loc); } return ans; } int main() { int z,m,ncase = 0; scanf("%d",&z); while(z--) { memset(c,0,sizeof(c)); printf("Case %d:\n",++ncase); scanf("%d%d",&n,&m); for(int i=0;i<n;i++) { scanf("%d",&A[i].h); A[i].id = i+1; } for(int j=0;j<m;j++) { scanf("%d%d%d",&Q[j].l,&Q[j].r,&Q[j].h); Q[j].id = j; } sort(A,A+n); sort(Q,Q+m); for(int j=0,i=0;j<m;j++) { while(i<n && A[i].h <= Q[j].h) { add(A[i].id,1); i++; } ans[Q[j].id] = sum(Q[j].r+1) - sum(Q[j].l); } for(int j=0;j<m;j++) printf("%d\n",ans[j]); } return 0; }