Turing Tree HDU - 3333 线段树 离线处理

题解

题目大意 给你n个数字 问你区间内不同元素的和

将讯问离线处理 按照右端点排序 遍历每个位置用一个数组记录每个数值的出现 如果没出现过则在线段树中出现位置标记出现 如果出现则删除上次线段树中的标记并更新标记
对于每个查询当遍历到查询右端点时区间求和则为答案

AC代码

#include <stdio.h>
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;

const int INF = 0x3f3f3f3f;
const int MAXN = 3e4 + 10;
const int MAXQ = 1e5 + 10;
int a[MAXN], vis[MAXN];
ll ans[MAXQ];
vector<int> dz;

struct node2
{
	int l, r;
	int num;
	bool operator < (const node2 &oth)
	{
		return this->r < oth.r;
	}
}q[MAXQ];
struct node 
{
	int l, r;
	ll v;
}tre[MAXN * 4];

inline void PushUp(int x)
{
	tre[x].v = tre[x << 1].v + tre[x << 1 | 1].v;
}
void Build(int x, int l, int r)
{
	tre[x].l = l, tre[x].r = r, tre[x].v = 0;
	if (l == r)
		return;
	else
	{
		int m = l + r >> 1;
		Build(x << 1, l, m);
		Build(x << 1 | 1, m + 1, r);
	}
}
void Update(int x, int p, ll v)
{
	int l = tre[x].l, r = tre[x].r;
	if (l == r)
		tre[x].v += v;
	else
	{
		int m = l + r >> 1;
		if (m >= p)
			Update(x << 1, p, v);
		else
			Update(x << 1 | 1, p, v);
		PushUp(x);
	}
}
ll Query(int x, int pl, int pr)
{
	int l = tre[x].l, r = tre[x].r;
	if (pl <= l && r <= pr)
		return tre[x].v;
	else
	{
		int m = l + r >> 1;
		ll v = 0;
		if (m >= pl)
			v += Query(x << 1, pl, pr);
		if (m < pr)
			v += Query(x << 1 | 1, pl, pr);
		return v;
	}
}
int Dis(int v)
{
	return lower_bound(dz.begin(), dz.end(), v) - dz.begin();
}
int main()
{
#ifdef LOCAL
	freopen("C:/input.txt", "r", stdin);
#endif
	int T;
	cin >> T;
	while (T--)
	{
		memset(vis, 0, sizeof(vis));
		dz.clear();
		dz.push_back(-INF);
		int N, Q;
		cin >> N;
		for (int i = 1; i <= N; i++)
			scanf("%d", &a[i]), dz.push_back(a[i]);
		sort(dz.begin(), dz.end());
		dz.erase(unique(dz.begin(), dz.end()), dz.end()); //离散化值
		cin >> Q;
		for (int i = 1; i <= Q; i++)
			scanf("%d%d", &q[i].l, &q[i].r), q[i].num = i;
		sort(q + 1, q + Q + 1);
		Build(1, 1, N);
		int j = 1;
		for (int i = 1; i <= N; i++) //遍历数列
		{
			int k = vis[Dis(a[i])];
			if (k) //出现过
				Update(1, k, -a[i]); //取消上次出现位置标记
			Update(1, i, a[i]); //在当前位置标记新数字的值
			vis[Dis(a[i])] = i;
			while (j <= Q && q[j].r == i) //答案右端点等于当前位置
				ans[q[j].num] = Query(1, q[j].l, i), j++; //查询区间和
		}
		for (int i = 1; i <= Q; i++)
			printf("%lld\n", ans[i]);
	}

	return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值