原题:
题目链接:http://acm.hdu.edu.cn/webcontest/contest_showproblem.php?cid=11842&pid=1006&ojid=0&hide=1&problem=Problem%20F
题意:
给出n个数,给出m个区间,让求这一区间的元素和。
如果有一样的数字,只算一遍。
例如Sample 2,1 2 区间段和是1.
思路:求区间段和,首先想到的就是用树状数组。那么怎么考虑一样的数字的问题呢?就要用到离线处理了。首先将所求区间和进行储存并记录其出现的初始位置。然后对所有区间按照右端点进行排序。然后用一个变量进行记录。如果该元素之前出现过,那么在之前位置往后减去该元素,然后再在当前位置后加上该元素。直到区间结束,更新过后求结果。对结果进行保存。
源代码:
/*
1006
离线处理。
先将区间保存,然后按照右端点升序排列。
离线处理要记录初始的区间位置
然后从头开始查找。若该点的元素出现过,则从原来位置开始减去该元素。
若没有出现过,则加上该元素。
最后求和。
*/
#include
#include
#include
#include
#include
#define MAX 1000000
using namespace std;
long long C[MAX];
long long a[MAX];
long long flag[MAX];
long long jieguo[MAX];
struct Qujian
{
int i;
int j;
int id;
}qujian[MAX];
bool cmp(Qujian a,Qujian b)
{
return a.j < b.j;
}
long long lowbit(long long x)
{
return x & (-x);
}
long long getsum(long long x)
{
long long sum = 0;
while (x > 0)
{
sum += C[x];
x -= lowbit(x);
}
return sum;
}
void add(long long x,long long v)
{
while (x < MAX)
{
C[x] += v;
x += lowbit(x);
}
}
int main()
{
int n;
scanf("%d",&n);
while (n--)
{
int m;
memset(flag,0,sizeof(flag));
memset(C,0,sizeof(C));
memset(a,0,sizeof(a));
scanf("%d",&m);
for (int i = 1; i <= m; i++)
scanf("%lld",&a[i]);
int k;
scanf("%d",&k);
for (int p = 1; p <= k; p++)
{
scanf("%d%d",&qujian[p].i,&qujian[p].j);
qujian[p].id = p;
}
sort(qujian+1,qujian+k+1,cmp);
//for (int i = 1; i <= k; i++)
//{
// cout << qujian[i].i << endl << qujian[i].j << endl;
// cout << qujian[i].id << endl;
//}
long long flag1 = 1;
for (int i = 1; i <= k; i++)
{
while (flag1 <= qujian[i].j)
{
long long b;
b = a[flag1];
if (flag[b])
add(flag[b],-b);
add(flag1,b);
flag[b] = flag1;
flag1++;
}
//cout << getsum(qujian[i].j) << endl << getsum(qujian[i]. i - 1)<< endl;
jieguo[qujian[i].id] = getsum(qujian[i].j) - getsum(qujian[i].i-1);
}
for (int i = 1; i <= k; i++)
printf("%lld\n",jieguo[i]);
}
return 0;
}