天天看点

HDU 3874 Necklace 2011 Multi-University Training Contest 4 - Host by SDU 树状数组+离散化

/*
题意为查找区间去重后的和
用树状数组离线处理
将所有查询以右端点从小到大排序
按此顺序边去重边查询
前面的去重就不会影响到后面的结果了
*/
#include <iostream>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<algorithm>
#include<ctime>
using namespace std;
#define N 50009
int n,m;
struct INV
{
	int s,e,num;
}inv[200009];//询问区间s为起点,e为终点
int first[N*20],pre[N],tmp[N];//pre[i]表示第i个数上次出现的位置,i位置前无与tmp[i]相同的数则为-1
long long ans[200009];
bool cmp(INV x,INV y)//以区间右侧从小到大排序
{
	if(x.e!=y.e)return x.e<y.e;
	return x.s<y.s;
}
//刚学到的输入方式,能优化时间哦~~
int nextInt() {
    char c;
    while (c = getchar(), c < '0' || c > '9');
    int r = c - '0';
    while (c = getchar(), c >= '0' && c <= '9') r = r * 10 + c - '0';
    return r;
}

//以下为树状数组模版
//******************************************************

long long ar[N];
int lowb(int t){return t&(-t);}
void add(int i,int v)
{
	for(;i<N;ar[i]+=v,i+=lowb(i));
}
long long sum(int i)
{
	long long s=0;
	for(;i>0;s+=ar[i],i-=lowb(i));
	return s;
}
//***********************************************************
void init()
{
	scanf("%d",&n);
	memset(ar,0,sizeof(ar));
	memset(first,-1,sizeof(first));
	for(int i=1;i<=n;i++)
	{
		//scanf("%d",&tmp[i]);
		tmp[i]=nextInt();
		pre[i]=first[tmp[i]];
		first[tmp[i]]=i;
		add(i,tmp[i]);
	}
	scanf("%d",&m);
	for(int i=0;i<m;i++)
	{
		//scanf("%d%d",&inv[i].s,&inv[i].e);
		inv[i].s=nextInt();
		inv[i].e=nextInt();
		if(inv[i].s>inv[i].e)swap(inv[i].s,inv[i].e);
		inv[i].num=i;
	}
}
void solve()
{
	sort(inv,inv+m,cmp);
	int r=0;  //r的左侧已经去重完毕
	for(int i=0;i<m;i++)
	{
		for(int j=inv[i].e;j>r;j--)//讲要查询的未去重的地段去重
		{
			if(pre[j]!=-1)
			add(pre[j],-tmp[j]);
		}
		r=inv[i].e;
		ans[inv[i].num]=sum(inv[i].e)-sum(inv[i].s-1);
	}
	
	for(int i=0;i<m;i++)
	printf("%I64d\n",ans[i]);
}
int main()
{
    int Case;
	scanf("%d",&Case);
	while(Case--)
	{
		init();
		solve();
	}
    return 0;
}