题目:http://acm.hdu.edu.cn/showproblem.php?pid=3333

题意:给定一个长度为n的数组,有一些询问:[l, r]内元素的和(相同元素只加一次)。

思路:之前做过类似的题目,用的莫队算法或者主席树,也是学习这两个的过程中看到了树状数组离线算法,这次用树状数组离线乱搞一下。首先对所有的询问按照右端点从小到大排序,然后开始依次处理每个元素,对于当前元素,若是第一次出现,就更新到树状数组中,并且记录下这个元素在数组中的位置(以后可能要删除),若不是第一次碰到这个元素,首先从树状数组里删除之前的(我们记录了之前的位置),然后再从当前位置更新到树状数组里面去,当然也要记录下当前元素的位置。若当前处理的位置等于询问的右端点,那么可以处理这个询问了,结果就是sum(r) - sum(l-1)

总结:树状数组竟有如此巧妙的使用

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
using namespace std;

typedef long long ll;
const int N = 30010;
ll bit[N], res[N*4];
int a[N], b[N], vis[N];
int n, m;
struct node
{
    int l, r, id;
}g[N*4];
bool cmp(node a, node b)
{
    return a.r < b.r;
}
void add(int i, int x)
{
    while(i <= n) bit[i] += x, i += i & -i;
}
ll sum(int i)
{
    ll s = 0;
    while(i > 0) s += bit[i], i -= i & -i;
    return s;
}
int main()
{
    int t;
    scanf("%d", &t);
    while(t--)
    {
        scanf("%d", &n);
        for(int i = 1; i <= n; i++) scanf("%d", &a[i]), b[i] = a[i];
        sort(b+1, b+1+n);
        for(int i = 1; i <= n; i++) a[i] = lower_bound(b+1, b+1+n, a[i]) - b;
        scanf("%d", &m);
        for(int i = 1; i <= m; i++) scanf("%d%d", &g[i].l, &g[i].r), g[i].id = i;
        sort(g+1, g+1+m, cmp);
        memset(bit, 0, sizeof bit);
        memset(vis, 0, sizeof vis);
        int tot = 1;
        for(int i = 1; i <= n; i++)
        {
            if(vis[a[i]] == 0) add(i, b[a[i]]), vis[a[i]] = i;
            else
            {
                add(vis[a[i]], -b[a[i]]);
                add(i, b[a[i]]);
                vis[a[i]] = i;
            }
            while(tot <= m && g[tot].r == i)
                res[g[tot].id] = sum(g[tot].r) - sum(g[tot].l-1), tot++;
        }
        for(int i = 1; i <= m; i++) printf("%lld\n", res[i]);
    }
    return 0;
}