題目鏈接: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;
}