题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=3874
题目大意:区间[i, j]所有不同数字的和
算法: 离线算法,线段树, map
思路:先把要计算的区间按照一定顺序排好,先计算前面的区间,再计算后面的区间;再利用离线算法的思想,边删边加,把前面出现过的数所在位置变成0,把这个数加到现在出现的位置上。
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<map>
using namespace std;
#define lson l, m, rt << 1
#define rson m+1, r, rt << 1 | 1
#define mid int m = (r+l) >> 1
#define LL __int64
const int Max = 50000;
struct nod
{
int l, r;
int rd;
bool operator < (const nod &cmp) const
{
return r < cmp.r;
}
}p[898989];
int a[893456];
LL cnt[898989];
map<int, int> h;
LL s[898989];
void PushUp(int rt)
{
cnt[rt] = cnt[rt << 1] + cnt[rt << 1 | 1];
}
void update(int d, int k, int l, int r, int rt)
{
if(l == r)
{
cnt[rt] += k;
return ;
}
mid ;
if(d <= m)
update(d, k, lson);
else update(d, k, rson);
PushUp(rt);
}
LL query(int L, int R, int l, int r, int rt)
{
if(L <= l && r <= R)
{
return cnt[rt];
}
mid ;
LL ret = 0;
if(L <= m)
ret += query(L, R, lson);
if(m < R)
ret += query(L, R, rson);
return ret ;
}
int main()
{
int T, n, i, m;
scanf("%d", &T);
while(T --)
{
memset(cnt, 0, sizeof(cnt));
scanf("%d", &n);
for(i = 1; i <= n; i ++)
scanf("%d", &a[i]);
scanf("%d", &m);
for(i = 1; i <= m; i ++)
{
scanf("%d%d", &p[i].l, &p[i].r);
p[i].rd = i;
}
h.clear();
sort(p+1, p+1+m);
int temp = 1;
for(i = 1; i <= m; i ++)
{
while(temp <= p[i].r)
{
if(h[a[temp]])
{
// printf("a[temp] = %d\n", a[temp]);
// printf("temp = %d, h[a[temp]] = %d\n", temp, h[a[temp]]);
update(h[a[temp]], -a[temp], 1, Max, 1);
}//删数
update(temp, a[temp], 1, Max, 1);//加数
h[a[temp]] = temp;
temp ++;
}
s[p[i].rd ] = query(p[i].l, p[i].r, 1, Max, 1);
}
for(i = 1; i <= m; i ++)
printf("%I64d\n", s[i]);
}
return 0;
}