题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4417
题意:给出一个长度为n的数列,询问m次,L到R之间不大于H的数的个数。
解析:暴力是没戏,考虑的时候我们希望得到这样的数据,array[i]表示i个元素之前小于H的元素个数(对任意H),这样每次询问结果就是array[R]-array[L],用求逆序数的思想,利用树状数组可以求出在第i个元素之前有多少个小于H的,这样我们可以从小到大枚举每一个i,要求的是,对于区间左端点等于i的询问,求出i之前有多少个小于对应区间的H,对于区间右端点等于i的,求出i之前有多少个小于H的,更新完之后,每个区间的左右端点对应的值肯定都求出来了,右端点减去左端点的值即为结果。在找哪些区间端点等于i的时候最好用vector存一下,具体代码中再解释吧,这题不太好说,另外数据比较大,需要对H的值哈希一下。
自己再多想想啊,这题目不太好解释。
参考代码:
#include<iostream>
#include<cstdio>
#include<vector>
#include<cstring>
#include<algorithm>
using namespace std;
#define clr(arr,v) memset(arr,v,sizeof(arr))
const int M = 100500;
template<int size,int Max>
class Hash{
public:
Hash(){}
void clear(){
clr(h,-1);
pos = 0;
}
int& operator[](int x){
return val[ add(x) ];
}
int add(int x){
int cur = x % Max,value = x / Max;
for(int i = h[cur]; i != -1 ;i = next[i])
{
if(num[i] == value) return i;
}
num[++pos] = value;
next[pos] = h[cur];
h[cur] = pos;
return pos;
}
private:
int h[Max],num[size],next[size],val[size],pos;
};
Hash<M,1000007> h;
struct segment{
int left,right,H,index;
int value_l,value_r;
}seg[M];
struct point{
point(){}
point(int _index,int _H,int _or):index(_index),H(_H),or(_or){}
int index,H,or;
};
vector<point> vec[M];
int tree[M],num[M],data[M],pos;
void read(int n,int m)
{
clr(seg,0);
for(int i = 0;i <= n;++i)
vec[i].clear();
pos = 0;
for(int i = 1;i <= n;++i)
{
scanf("%d",&data[i]);
num[pos++] = data[i];
}
for(int i = 0;i < m;++i)
{
scanf("%d%d%d",&seg[i].left,&seg[i].right,&seg[i].H);
seg[i].left++;
seg[i].right++;
seg[i].index = i;
vec[ seg[i].left ].push_back(point(seg[i].index,seg[i].H,0));
vec[ seg[i].right ].push_back(point(seg[i].index,seg[i].H,1));
num[pos++] = seg[i].H;
}
}
int lowbit(int x) { return x&-x; }
int get_num(int x,int n)
{
if(x >= n) return tree[n];
return tree[x] + get_num(x+lowbit(x),n);
}
void update(int x)
{
if(x == 0) return ;
tree[x]++;
update(x-lowbit(x));
}
void solve(int n,int m)
{
clr(tree,0);
int index = 0,res;
sort(num,num+pos);
for(int i = 0;i < pos;++i) //哈希
{
if(i == 0 || num[i] != num[i-1])
h[ num[i] ] = ++index;
else
h[ num[i] ] = index;
}
for(int i = 1;i <= n;++i)
{
for(int j = 0;j < vec[i].size();++j)
{
if(vec[i][j].or == 0)
{
res = i - 1 - get_num(h[ vec[i][j].H ],index); //计算的是array[R]-array[L-1]的值,所以是i-1
seg[ vec[i][j].index ].value_l = res; //value_l表示L之前小于H的个数
}
}
update(h[ data[i] ]-1); //要在计算完左端点后更新
for(int j = 0;j < vec[i].size();++j)
{
if(vec[i][j].or)
{
res = i - get_num(h[ vec[i][j].H ],index);
seg[ vec[i][j].index ].value_r = res; // value_r表示R(包含)之前小于H的个数
}
}
}
for(int i = 0;i < m;++i)
printf("%d\n",seg[i].value_r - seg[i].value_l);
}
int main()
{
//freopen("1.txt","r",stdin);
int T,n,m,ncase = 1;
scanf("%d",&T);
while(T--)
{
h.clear();
scanf("%d%d",&n,&m);
read(n,m);
printf("Case %d:\n",ncase++);
solve(n,m);
}
return 0;
}