Hduoj3874 【离线+树状数组】

/*Necklace
Time Limit: 15000/5000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)
Total Submission(s): 3216    Accepted Submission(s): 1108

Problem Description
Mery has a beautiful necklace. The necklace is made up of N magic balls. Each ball has a beautiful value. The balls with the same beautiful 
value look the same, so if two or more balls have the same beautiful value, we just count it once. We define the beautiful value of some 
interval [x,y] as F(x,y). F(x,y) is calculated as the sum of the beautiful value from the xth ball to the yth ball and the same value is ONLY
 COUNTED ONCE. For example, if the necklace is 1 1 1 2 3 1, we have F(1,3)=1, F(2,4)=3, F(2,6)=6.

Now Mery thinks the necklace is too long. She plans to take some continuous part of the necklace to build a new one. She wants to know each 
of the beautiful value of M continuous parts of the necklace. She will give you M intervals [L,R] (1<=L<=R<=N) and you must tell her F(L,R) 
of them.

Input
The first line is T(T<=10), representing the number of test cases.
For each case, the first line is a number N,1 <=N <=50000, indicating the number of the magic balls. The second line contains 
N non-negative integer numbers not greater 1000000, representing the beautiful value of the N balls. The third line has a number M,
1 <=M <=200000, meaning the nunber of the queries. Each of the next M lines contains L and R, the query.

Output
For each query, output a line contains an integer number, representing the result of the query.
 
Sample Input
2
6
1 2 3 4 3 5
3
1 2
3 5
2 6
6
1 1 1 2 3 5
3
1 1
2 4
3 5
 
Sample Output
3
7
14
1
3
6
 
Source
2011 Multi-University Training Contest 4 - Host by SDU 
*/ 
// 离线  树状数组 
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
int a[50010], b[1000010] ;//b记录当前数值的位置,a原数据,c树状数组,ans查询结果 
__int64 c[50010], ans[200010];
struct node
{
	int l, r, id;
}query[200010];//离线查询中间保存数组 
int cmp(const void*a, const void *b)
{
	return (*(struct node*)a).r - (*(struct node *) b).r;
}
__int64 sum(int x, int n)
{
	__int64 sum = 0;
	while(x > 0)
	{
		sum += c[x];
		x -= x&(-x);
	}
	return sum;
}
void update(int i, int n, int x)
{
	while(i <= n)
	{
		c[i] += x;
		i += i&(-i);
	}
}
int main()
{
	int T;
	scanf("%d", &T);
	while(T--)
	{
		int n, m;
		memset(b, 0, sizeof(b));
		memset(c, 0, sizeof(c));
		scanf("%d", &n);
		for(int i = 1; i <= n; i++)//输入原始数据 
		scanf("%d", &a[i]);
		scanf("%d", &m);
		for(int i = 1; i <= m; i++)//输入要访问的区间
		{ 
			scanf("%d%d",&query[i].l, &query[i].r);
			query[i].id  = i;
		}
		qsort(query+1, m, sizeof(query[0]), cmp);//从小到大排序 
		int j = 1;
		for(int i = 1; i <= m; i++)//计算每一个查询 
		{
			while(j <= query[i].r)//当当前的a【j】 小于 询问的右端点 
			{
				if(b[a[j]] != 0)//如果a【j】 数值在a数组前面已经存在, 
				{				//那么更新树状数组,从前面的数值所在位置开始往后将树状数组中的值减去 
					update(b[a[j]], n, -a[j]);
				}
				b[a[j]] = j;// 保存a【j】数值的位置 
				update(j, n, a[j]);//更新当前位置j后面的树状数组 
				j++;
			}
			ans[query[i].id] = sum(query[i].r, n) - sum(query[i].l-1, n);//当j = 查询的右端点      
		} 							//此时已经保证当前j值前面的树状数组不会存在重复数值的情况 ,计算查询结果 
		for(int i = 1; i <= m; i++)//按输入时的顺序输出查询结果 
		printf("%I64d\n", ans[i]);
	}
	return 0;
}

题意 :给出一个数组,计算区间数字的和,有个要求就是相同的数字只计算一次

思路:本题采用了树状数组 + 离线的思想,树状数组用于节省时间,离线可以帮助计算非重复数字的和。

中途采用了一个数组来保存输入的数字在元数组中的最后一个位置,离线的时候可以将查询的端点从小到大排序,这样计算答案时不会影响到后面的区间段,因为树状数组中

保存的 是无重复的数字之和,所以若是不对查询的区间段进行排序,计算出来的答案是错误的。

难点:必须得想出用离线数组的方法来计算非重复区间段的和,否则用原始的树状数组难以计算出答案。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值