HDU 3333 Turing Tree 线段树+离线处理
题目链接:
http://acm.hdu.edu.cn/showproblem.php?pid=3333
Turing Tree
Memory Limit: 32768/32768 K (Java/Others)
题意
求一段区间的所有不同的数字的和。
题解
这题要考虑离线处理查询。
有多个相同的数字这么办?
我们可以考虑在每个时刻,我们插入的数据都是不同的,我们可以从左到右插入一个数据的时候,我们先判断一下它是否已经插入过了,如果已经插入了我们就把之前的位置的那个数删点,同时插入现在这个位置(相等于是往右边转移了这个数),同时我们吧查询按照右端点升序排,先处理右端点比较小的查询,这样就能保证正确性了。
代码
#include<iostream>
#include<cstdio>
#include<map>
#include<algorithm>
#include<cstring>
#define lson (o<<1)
#define rson ((o<<1)|1)
#define M l+(r-l)/2
using namespace std;
const int maxn = 3e4 + 10;
const int maxq = 1e5 + 10;
typedef long long LL;
LL sumv[maxn << 2];
map<int, int> mp;
int arr[maxn];
LL ans[maxq];
struct Node {
int l, r, id;
bool operator <(const Node& tmp) const {
return r < tmp.r;
}
}nds[maxq];
int ql, qr;
LL _sumv;
void query(int o, int l, int r) {
if (ql <= l&&r <= qr) {
_sumv += sumv[o];
}
else {
if (ql <= M) query(lson, l, M);
if (qr>M) query(rson, M + 1, r);
}
}
int _p, _v;
void update(int o, int l, int r) {
if (l == r) {
sumv[o] = _v;
}
else {
if (_p <= M) update(lson, l, M);
else update(rson, M + 1, r);
sumv[o] = sumv[lson] + sumv[rson];
}
}
int n;
void init() {
memset(sumv, 0, sizeof(sumv));
mp.clear();
}
int main() {
int tc;
scanf("%d", &tc);
while (tc--) {
init();
scanf("%d", &n);
for (int i = 1; i <= n; i++) {
scanf("%d", &arr[i]);
}
int q; scanf("%d", &q);
for (int i = 0; i < q; i++) {
scanf("%d%d", &nds[i].l, &nds[i].r);
nds[i].id = i;
}
sort(nds, nds + q);
int pos = 1;
for (int i = 0; i < q; i++) {
int l = nds[i].l, r = nds[i].r, id = nds[i].id;
while (pos <= r) {
if (mp.count(arr[pos])) {
_p = mp[arr[pos]], _v = 0;
update(1, 1, n);
}
mp[arr[pos]] = pos;
_p = pos, _v = arr[pos];
update(1, 1, n);
pos++;
}
ql = l, qr = r; _sumv = 0;
query(1, 1, n);
ans[id] = _sumv;
}
for (int i = 0; i < q; i++) {
printf("%lld\n", ans[i]);
}
}
return 0;
}
乱七八糟
对于一个区间里面的相同的数字,是我们不想要的,我们要达到的目的就是要去除它们的影响,删除它们是一种不错的解决办法,那么我们对于一个出现多次的数,就只要保留一个,保留哪一个呢?第一个想到的自然是最左边和最右边,而之后调整区间的操作,也是完全为了配合我们删除插入顺序对结果的影响而做出的调整。
之前也有类似的,如用数状数组计算排名,对于比它大的数,是我们不想要的,插入它的时候,我们只需要知道比它小的数有多少个,一种解决方案就是给每个数据按从小到大从左到右先安个空位置,插入的时候对号入座,那比当前插入的数小的自然就会跑到左边,我们可以用树状数组或线段树来维护。
离线处理一个比在线做的优势就是!!!我们能!调整!处理的顺序!