Turing Tree
Time Limit: 6000/3000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 3981 Accepted Submission(s): 1349
Problem Description
After inventing Turing Tree, 3xian always felt boring when solving problems about intervals, because Turing Tree could easily have the solution. As well, wily 3xian made lots of new problems about intervals. So, today, this sick thing happens again...
Now given a sequence of N numbers A1, A2, ..., AN and a number of Queries(i, j) (1≤i≤j≤N). For each Query(i, j), you are to caculate the sum of distinct values in the subsequence Ai, Ai+1, ..., Aj.
Input
The first line is an integer T (1 ≤ T ≤ 10), indecating the number of testcases below.
For each case, the input format will be like this:
* Line 1: N (1 ≤ N ≤ 30,000).
* Line 2: N integers A1, A2, ..., AN (0 ≤ Ai ≤ 1,000,000,000).
* Line 3: Q (1 ≤ Q ≤ 100,000), the number of Queries.
* Next Q lines: each line contains 2 integers i, j representing a Query (1 ≤ i ≤ j ≤ N).
Output
For each Query, print the sum of distinct values of the specified subsequence in one line.
Sample Input
2
3
1 1 4
2
1 2
2 3
5
1 1 2 1 3
3
1 5
2 4
3 5
Sample Output
1
5
6
3
6
Author
[email protected]
Source
HDOJ Monthly Contest – 2010.03.06
喵呜,离散树状数组。
这道题由于相同的值加和的时候只算一次,所以比较伤脑筋==
怎么办呢?
我们发现对于一个值,由于相同的只算一次,所以在任意时间内,这个值只需要出现一次。
如果我们从作往右将原数组更新到树状数组,如果某个值之前出现过,那么我在更新当前的时候,还需要删掉之前那个。
这样就可以保证当前的序列中一个值只出现了一次,而且位置更新成最后出现的位置。
如果只出现一次的话那就又是我们喜闻乐见的那个熟悉的树状数组了呢 喵呜!
但是这样删掉前面出现的元素真心大丈夫? 万一之后又查询了之前你删掉的点岂不是整个人都萌萌哒了?
所以我们可以按照查询区间的又断点排序,保证前面删掉的点以后不会再被查询。
也就是按照顺序,从左往右,边查询,边更新原数组到树状数组。
由于查询区间是无序的,按照右端点排序之后打乱了原来的查询顺序,所以要离线操作。
由于查询区间是无序的,按照右端点排序之后打乱了原来的查询顺序,所以要离线操作。
由于查询区间是无序的,按照右端点排序之后打乱了原来的查询顺序,所以要离线操作。
重要的事情说三遍。
所谓离线操作,就是查询的时候不直接输出答案,而是将答案存起来,然后最后一起输出。
我们需要开一个数组pre [x] 记录x这个数的上一个位置,初始为-1
由 x的范围太大,数组存不下,所以要离散化。
离散化是为了记录一个数上次出现的位置!
注意要开LL 。
/************************************************************************* > File Name: code/hdu/3333.cpp > Author: 111qqz > Email: [email protected] > Created Time: 2015年08月07日 星期五 17时04分07秒 ************************************************************************/ #include<iostream> #include<iomanip> #include<cstdio> #include<algorithm> #include<cmath> #include<cstring> #include<string> #include<map> #include<set> #include<queue> #include<vector> #include<stack> #define y0 abc111qqz #define y1 hust111qqz #define yn hez111qqz #define j1 cute111qqz #define tm crazy111qqz #define lr dying111qqz using namespace std; #define REP(i, n) for (int i=0;i<int(n);++i) typedef long long LL; typedef unsigned long long ULL; const int inf = 0x7fffffff; const int N=5E4+3; LL c[N]; LL n,m,qur; LL ref[N]; LL pre[N]; LL ori[N]; struct Q { LL val,id; }q[N]; struct S { LL x,y; LL id,ans; }s[200005]; bool cmp( Q a,Q b) { if (a.val<b.val) return true; return false; } bool cmp2(S a,S b) { if (a.y<b.y) return true; return false; } LL lowbit ( int x) { return x&(-x); } void update ( LL x,LL delta) { for ( LL i = x ; i <= n ; i = i + lowbit(i)) { c[i] = c[i] + delta; } } LL sum( LL x) { LL res = 0 ; for ( LL i = x; i >= 1 ; i = i - lowbit(i)) { res = res + c[i]; } return res; } int main() { int T; cin>>T; while (T--) { memset(ref,0,sizeof(ref)); memset(c,0,sizeof(c)); memset(pre,-1,sizeof(pre)); //标记上次出现位置的数组 scanf("%lld",&n); for ( LL i = 1 ; i <= n ; i++) { scanf("%lld",&q[i].val); q[i].id = i; } sort(q+1,q+n+1,cmp); LL cnt = 0; for ( LL i = 1 ; i <= n ; i++ ) { if (q[i].val!=q[i-1].val) { cnt++; } ref[q[i].id] = cnt; ori[q[i].id] = q[i].val; } scanf("%lld",&qur); for ( LL i = 1 ;i <= qur; i++ ) { scanf("%lld %lld",&s[i].x,&s[i].y); s[i].id = i; } sort(s+1,s+1+qur,cmp2); s[0].y = 0; for ( LL i = 1 ; i <= qur ; i++) { for ( LL j = s[i-1].y+1 ; j <= s[i].y ; j++) { int tmp = ref[j]; if (pre[tmp]==-1) { update(j,ori[j]); } else { update (j,ori[j]); update (pre[tmp],-ori[j]); } pre[tmp] = j; } s[s[i].id].ans = sum(s[i].y)-sum(s[i].x-1); } for ( int i = 1 ; i <= qur ; i++ ) { cout<<s[i].ans<<endl; } } return 0; }