换个角度思考(离线化+树状数组)

换个角度思考

题目描述
给定一个序列,有多次询问,每次查询区间里小于等于某个数的元素的个数
即对于询问 (l,r,x),你需要输出 ∑ i = l r [ a i ≤ x ] \sum_{i=l}^{r}[a_i \le x] i=lr[aix] 的值
其中 [exp] 是一个函数,它返回 1 当且仅当 exp 成立,其中 exp 表示某个表达式
输入描述:
第一行两个整数n,m
第二行n个整数表示序列a的元素,序列下标从1开始标号,保证1 ≤ a_i ≤ 105
之后有m行,每行三个整数(l,r,k),保证1 ≤ l ≤ r ≤ n,且1 ≤ k ≤ 105
输出描述:
对于每一个询问,输出一个整数表示答案后回车


思路: 离线化后,将询问以x从小到大排序后再依次询问,这样每次后边的询问都能用前面的结果,将原数组也重新排序,并且都记录原始位置,这样每次能全部找完小于等于x的数,还是非常妙的。每次动态单点修改和查询区间和树状数组就够用了。(这是主席树的模板题?QAQ

和洛谷的 P1972 HH的项链 还是有很多相似之处的

#include
#define F first
#define S second
using namespace std;
const int N = 1e5+7; 

struct Node{
	int l,r;
	int i,x;
}q[N];

int tr[N];
int n,m;
pair<int,int> a[N];
int ans[N];

int lowbit(int x){
	return x&-x;
}

void add(int x,int v){
	for(int i = x;i<=n;i+=lowbit(i))
	tr[i] += v; 
}

int query(int x){
	int ans=0;
	for(int i=x;i;i-=lowbit(i)) ans+=tr[i];
	return ans;
}

bool cmp(Node a,Node b){
	return a.x<b.x;
}

int main()
{
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++) scanf("%d",&a[i].F),a[i].S = i;
	for(int i=1;i<=m;i++){
		scanf("%d%d%d",&q[i].l,&q[i].r,&q[i].x);
		q[i].i = i;
	}
	sort(a+1,a+1+n);
	sort(q+1,q+1+m,cmp);
	int p = 1;
	for(int i=1;i<=m;i++){
		while(a[p].F<= q[i].x && p<=n){
			add(a[p].S,1);
			p++;
		}
		ans[q[i].i] = query(q[i].r) - query(q[i].l-1);
	}
	for(int i=1;i<=m;i++) printf("%d\n",ans[i]);
	return 0;
}

你可能感兴趣的:(线段树&树状数组,牛客每日一题)