/*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; }
题意 :给出一个数组,计算区间数字的和,有个要求就是相同的数字只计算一次
思路:本题采用了树状数组 + 离线的思想,树状数组用于节省时间,离线可以帮助计算非重复数字的和。
中途采用了一个数组来保存输入的数字在元数组中的最后一个位置,离线的时候可以将查询的端点从小到大排序,这样计算答案时不会影响到后面的区间段,因为树状数组中
保存的 是无重复的数字之和,所以若是不对查询的区间段进行排序,计算出来的答案是错误的。
难点:必须得想出用离线数组的方法来计算非重复区间段的和,否则用原始的树状数组难以计算出答案。