离线处理,线段树,区间求和-线段树

Contest (nefu.edu.cn)
Problem:H

Time Limit:2000ms
Memory Limit:165535K

Description

有一个序列包含n个正整数,现在有m次询问,每次询问为:求(L,R)的区间中小于等于K的数的和?

Input

输入包含多组数据。每组数据,第一行为n,表示这个整数序列长为n(1<=n<=1e5)。第二行输入整数序列x1,x2,……,xn(1<=xi<=1e9)。第三行输入m(1<=m<=1e5)。接下来m行,每行输入L,R,K(1<=L<=R<=n,0<=K<=1e9)。

Output

每组数据首先输出“Case #x:”,x从1开始,然后m行输出每次询问的结果,具体格式请参考样例。

Sample Input

6
2 5 3 4 7 6
3
2 5 4
3 6 5
1 4 3

Sample Output

Case #1:
7
7
5

Hint

 

Source

CJJ

解析:离线处理;

将所有的条件和问题全部读入arr数组中,然后按k值从小到大对数组arr进行排序,最后按照数组arr从1到n+m进行建树,这要就能保证所建的线段树中当前的所有值均小于或等于当前的k值,因此我们只需要通过线段树求出从1到n的所有值的和即可。


#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<string>
#include<cstring>
#include<cmath>
#include<ctime>
#include<algorithm>
#include<utility>
#include<stack>
#include<queue>
#include<vector>
#include<set>
#include<map>

using namespace std;
typedef long long LL;
const int N = 1e5 + 5;
int n, m;
struct st {
	int l, r, k,num;
}arr[4*N];

struct st1 {
	int l,r;
	LL sum;
}tr[N*4];

typedef struct st2 {
	LL v, num;
}st2;

int cmp(const struct st& a, const struct st& b) {
	if (a.k == b.k)
		return a.num < b.num;
	return a.k < b.k;
}

void build(int p, int l, int r) {
	tr[p].l = l, tr[p].r = r;
	if (l == r) {
		return;
	}
	LL mid = (l + r) / 2;
	if (l <= mid)
		build(p * 2, l, mid);
	if (r > mid)
		build(p * 2 + 1, mid + 1, r);
}

void change(int p, int y, int x) {
	if (tr[p].l == tr[p].r) {
		tr[p].sum = (LL)x;
		return;
	}
	LL mid = (tr[p].l + tr[p].r) / 2;
	if (y <= mid)change(p * 2, y, x);
	if (y > mid)change(p * 2 + 1, y, x);
	tr[p].sum = tr[p * 2].sum + tr[p * 2 + 1].sum;
}

LL ask(int p, int l, int r) {
	if (l <= tr[p].l && r >= tr[p].r) {
		return tr[p].sum;
	}
	LL mid = (tr[p].l + tr[p].r) / 2;
	LL val = 0;
	if (l <= mid)val+=ask(p * 2, l, r);
	if (r > mid)val += ask(p * 2 + 1, l, r);
	return val;
}

int cmp1(const st2& a, const st2& b) {
	return a.num < b.num;
}

int main() {
	int cnt = 0;
	while (scanf("%d",&n)!=EOF) {
		memset(tr, 0, sizeof(tr));
		memset(arr, 0, sizeof(arr));
		cnt++;
		for (int i = 1; i <= n; i++) {
			scanf("%d", &arr[i].k);
			arr[i].num = i;
		}
		scanf("%d", &m);
		for (int i = n + 1; i <= m + n; i++) {
			scanf("%d%d%d", &arr[i].l, &arr[i].r, &arr[i].k);
			arr[i].num = i;
		}
		sort(arr + 1, arr + 1 + n + m, cmp);
		build(1, 1, n);
		vector<st2>p;
		for (int i = 1; i <= n + m; i++) {
			if (arr[i].l==0) {
				change(1, arr[i].num, arr[i].k);
			}
			else {
				LL t = ask(1, arr[i].l, arr[i].r);
				p.push_back({ t,arr[i].num });
			}
		}
		sort(p.begin(), p.end(), cmp1);
		printf("Case #%d:\n", cnt);
		for (int i = 0; i < m; i++) {
			printf("%lld\n", p[i].v);
		}
	}
	return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值