题目: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;
}