HDU 3874 Necklace(树状数组+离线处理)
题目大意
有一串长度为 n(1≤n≤50000) 的项链,项链上每块石头都有一个价值,在计算项链上一段连续石头的价值和的时候,相同价值的只计算一次
现在给你 m(1≤m≤200000) 个询问,每个询问要查询一段连续石头 [L, R] (1≤L≤R≤n)的价值和
做法分析
先把所有的查询保存,然后按照查询区间的右端点由小到大排序,然后离线处理:
对于一个具体的石头的价值 val,由于我们是按照查询的右端点从小到大排序的,我们当然就希望这个价值出现的位置越靠后(编号越大)越好了,这样就能够保证我们在查询一段区间的和的时候不会算漏了,也就是说,只保留每个价值最靠后的那一块石头,不断的向后加石头更新就行了,具体的说就是这样做的:
每加一块石头(编号是 id),看这个石头的 val 是否出现过,如果没有出现过,直接加进去就行,然后 last[val]=id
如果出现过,要先把 last[val] 这个位置的值减去 val,然后在更新,接着给 last[val] 更新:last[val]=id
如果当前的这块石头是某个查询 [L, R] 的右端点,那么,这个时候直接查询 [L, R] 这段区间的和就行
当然,我们也可以把按照左端点从大到小排序,不过这样的话,我们就得倒着扫石头了
参考代码
HDU 3874
1 #include <iostream> 2 #include <cstring> 3 #include <cstdio> 4 #include <algorithm> 5 6 using namespace std; 7 8 typedef __int64 LL; 9 const int N=50006; 10 11 struct Node 12 { 13 int L, R, id; 14 inline void init(int a, int b, int c) 15 { 16 L=a, R=b, id=c; 17 } 18 19 inline bool operator <(const Node &temp) const 20 { 21 if(R==temp.R) return L<temp.L; 22 else return R<temp.R; 23 } 24 } op[200006]; 25 int t, n, m, last[1000006], data[N]; 26 LL BIT[N]; 27 28 void update(int pos, int val) 29 { 30 for(; pos<N; pos+=pos&(-pos)) BIT[pos]+=val; 31 } 32 33 LL query(int pos) 34 { 35 LL sum=0; 36 for(; pos>0; pos-=pos&(-pos)) sum+=BIT[pos]; 37 return sum; 38 } 39 40 LL ans[200006]; 41 42 int main() 43 { 44 scanf("%d", &t); 45 for(int ca=1; ca<=t; ca++) 46 { 47 scanf("%d", &n); 48 for(int i=1; i<=n; i++) 49 { 50 scanf("%d", &data[i]); 51 last[data[i]]=-1; 52 } 53 scanf("%d", &m); 54 for(int i=0, a, b; i<m; i++) 55 { 56 scanf("%d%d", &a, &b); 57 if(a>b) swap(a, b); 58 op[i].init(a, b, i); 59 } 60 sort(op, op+m); 61 memset(BIT, 0, sizeof BIT); 62 for(int i=0, u=1; i<m; i++) 63 { 64 for(; u<=op[i].R; u++) 65 { 66 if(last[data[u]]!=-1) update(last[data[u]], -data[u]); 67 last[data[u]]=u; 68 update(u, data[u]); 69 } 70 ans[op[i].id]=query(op[i].R)-query(op[i].L-1); 71 } 72 for(int i=0; i<m; i++) printf("%I64d\n", ans[i]); 73 } 74 return 0; 75 }
题目链接 & AC通道