想了一下午没有想到好办法,看了一下别人思路,该题需要离线处理。
这个方法可以很好的处理重复数字的问题,用来求解无重复数字的区间和问题。
将询问的区间按照右端点排序,每次记录上一个区间的右端点R,然后遍历R到当前区间右端点,用一个数组记录每一个数字出现的位置,如果该数字出现过,那么删除上一次出现的位置的值,并更新到当前位置。 这样就可以有效的去重 。 由于整个过程只是遍历了一遍数组,所以平摊意义下的时间复杂度很低。
细节参见代码:
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<vector>
#include<map>
#include<list>
#include<cmath>
#include<set>
#include<queue>
using namespace std;
typedef long long ll;
const int maxn = 50000 + 3;
const int maxm = 200000 + 5;
int T,n,m,l,r,b[maxn],vis[1000000+1];
ll bit[maxn+5],ans[maxm];
struct Edge{
int l,r,id;
bool operator < (const Edge& rhs) const {
return r < rhs.r;
}
}a[maxm];
ll sum(int x) {
ll ret = 0;
while(x > 0) {
ret += bit[x]; x -= (x & -x);
}
return ret;
}
void add(int x,int d) {
while(x <= maxn) {
bit[x] += d; x += (x & -x);
}
}
int main() {
scanf("%d",&T);
while(T--) {
scanf("%d",&n);
memset(vis,0,sizeof(vis));
memset(bit,0,sizeof(bit));
for(int i=1;i<=n;i++) {
scanf("%d",&b[i]);
add(i,b[i]);
}
scanf("%d",&m);
for(int i=0;i<m;i++)
scanf("%d%d",&a[i].l,&a[i].r) , a[i].id = i;
sort(a,a+m);
int R = 0;
for(int i=0;i<m;i++) {
for(int j=R+1;j<=a[i].r;j++) {
if(vis[b[j]]) {
add(vis[b[j]],-b[j]);
vis[b[j]] = j;
}
else vis[b[j]] = j;
}
R = a[i].r;
ans[a[i].id] = sum(a[i].r)-sum(a[i].l-1);
}
for(int i=0;i<m;i++) printf("%I64d\n",ans[i]);
}
return 0;
}