题目链接:https://cn.vjudge.net/problem/HDU-3333
题目大意
给定长度为n的序列,m个询问,每次查询[L,R]范围内不同元素的和。
分析
离散化不说了,注意在求和时计算的是离散化前的值,不是离散后的。先将要查询的区间存下来,按照右端点从小到大的顺序,右端点相同时按左端点从小到大的顺序排序,用vis数组来记录每个元素最后出现的位置。每次把在位置q[i].r之前的数更新成最后出现的位置,再求和就可以了。
代码
#include<iostream>
#include<cstdio>
#include<string.h>
#include<algorithm>
using namespace std;
typedef long long ll;
const int N = 30010;
const int M = 1000010;
int n,m,cnt,a[N],b[N],c[N];
int vis[M];
ll bit[N*10],ans[M];
struct Ques{
int l,r,id;
}q[N<<2];
bool cmp(const Ques x, const Ques y)
{
if(x.r == y.r) return x.l < y.l;
return x.r < y.r;
}
int lowbit(int x)
{
return x & (-x);
}
void add(int x, int val)
{
while(x < 10*N)
{
bit[x] += val;
x += lowbit(x);
}
}
ll sum(int x)
{
ll ans = 0;
while(x > 0)
{
ans += bit[x];
x -= lowbit(x);
}
return ans;
}
int getid(int x)
{
return lower_bound(b,b+cnt+1,x) - b + 1;
}
int main()
{
int t;
scanf("%d", &t);
while(t--)
{
scanf("%d", &n);
for(int i=1;i<=n;i++)
{
scanf("%d", &a[i]);
c[i] = a[i];
}
sort(a+1,a+n+1);
cnt = 0;
b[cnt++] = a[1];
for(int i=2;i<=n;i++)
{
if(a[i] != a[i-1])
b[cnt++] = a[i];
}
scanf("%d", &m);
for(int i=1;i<=m;i++)
{
int l,r;
scanf("%d%d", &l, &r);
q[i].l = l;
q[i].r = r;
q[i].id = i;
}
sort(q+1, q+m+1, cmp);
memset(vis, -1, sizeof vis);
memset(bit, 0, sizeof bit);
memset(ans, 0, sizeof ans);
int num = 1;
for(int i=1;i<=m;i++)
{
while(num <= q[i].r)
{
int x = getid(c[num]);
if(vis[x] != -1)
add(vis[x], -c[num]);
add(num, c[num]);
vis[x] = num;
num++;
}
ans[q[i].id] = sum(q[i].r) - sum(q[i].l-1);
}
for(int i=1;i<=m;i++)
printf("%lld\n", ans[i]);
}
return 0;
}