题目链接:
http://acm.hdu.edu.cn/showproblem.php?pid=3333
Turing Tree
Time Limit: 6000/3000 MS (Java/Others)Memory Limit: 32768/32768 K (Java/Others)
#### 问题描述
> After inventing Turing Tree, 3xian always felt boring when solving problems about intervals, because Turing Tree could easily have the solution. As well, wily 3xian made lots of new problems about intervals. So, today, this sick thing happens again...
>
> Now given a sequence of N numbers A1, A2, ..., AN and a number of Queries(i, j) (1≤i≤j≤N). For each Query(i, j), you are to caculate the sum of distinct values in the subsequence Ai, Ai+1, ..., Aj.
#### 输入
> The first line is an integer T (1 ≤ T ≤ 10), indecating the number of testcases below.
> For each case, the input format will be like this:
> * Line 1: N (1 ≤ N ≤ 30,000).
> * Line 2: N integers A1, A2, ..., AN (0 ≤ Ai ≤ 1,000,000,000).
> * Line 3: Q (1 ≤ Q ≤ 100,000), the number of Queries.
> * Next Q lines: each line contains 2 integers i, j representing a Query (1 ≤ i ≤ j ≤ N).
#### 输出
> For each Query, print the sum of distinct values of the specified subsequence in one line.
#### 样例
> **sample input**
> 2
> 3
> 1 1 4
> 2
> 1 2
> 2 3
> 5
> 1 1 2 1 3
> 3
> 1 5
> 2 4
> 3 5
>
> **sample output**
> 1
> 5
> 6
> 3
> 6
题意
求一段区间的所有不同的数字的和。
题解
这题要考虑离线处理查询。
有多个相同的数字这么办?
我们可以考虑在每个时刻,我们插入的数据都是不同的,我们可以从左到右插入一个数据的时候,我们先判断一下它是否已经插入过了,如果已经插入了我们就把之前的位置的那个数删点,同时插入现在这个位置(相等于是往右边转移了这个数),同时我们吧查询按照右端点升序排,先处理右端点比较小的查询,这样就能保证正确性了。
代码
#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;
}
乱七八糟
对于一个区间里面的相同的数字,是我们不想要的,我们要达到的目的就是要去除它们的影响,删除它们是一种不错的解决办法,那么我们对于一个出现多次的数,就只要保留一个,保留哪一个呢?第一个想到的自然是最左边和最右边,而之后调整区间的操作,也是完全为了配合我们删除插入顺序对结果的影响而做出的调整。
之前也有类似的,如用数状数组计算排名,对于比它大的数,是我们不想要的,插入它的时候,我们只需要知道比它小的数有多少个,一种解决方案就是给每个数据按从小到大从左到右先安个空位置,插入的时候对号入座,那比当前插入的数小的自然就会跑到左边,我们可以用树状数组或线段树来维护。
离线处理一个比在线做的优势就是!!!我们能!调整!处理的顺序!