题目链接:Necklace
题意:
给出一串珠子,每个珠子有它的value,现在给出n(n<=5e4)个珠子的value(1<=value<=1e6),现在给出m(1<=m<=2e5)个询问,每个询问给出l和r,要求[l,r]区间内所有珠子的value(如果有相同value值的珠子则只计算一次这个珠子的值)。
题解:
刚开始看到这题,遇到区间求和就想到了树状数组。但是如何解决区间内相同value值只能计算一次的问题呢?看了题解后发现可以用离线化解决这个问题,我们可以先把所有的询问保存下来,将询问按右端点从小到大排序,然后将所有有重复的value值只保留最后一个位置的值,其他的value值全都减去。因为区间的右端点保证是从小到大的,而可以发现[l,r]这个区间的值就等于所有数保留最后一个出现的数的和(可以自己验证一下)。这里离散化最大的功能就是保证了我们的操作是从小到大的,这样我们前面的操作就不会影响后面的操作。还有这里是要用long long的不然会爆~@。@#
1 #include<cstdio> 2 #include<cstring> 3 #include<iostream> 4 #include<algorithm> 5 using namespace std; 6 const int MAX_N = 1e5+9; 7 typedef pair<int,int> P; 8 P q[MAX_N*4]; 9 int q1[MAX_N*4]; 10 int vis[MAX_N*10]; 11 long long vec[MAX_N]; 12 long long res[MAX_N]; 13 long long out[MAX_N*4]; 14 void add(int x,long long num) 15 { 16 for(;x<MAX_N;x+=(x&-x)) 17 res[x] += num; 18 } 19 long long sum(int x) 20 { 21 long long ans = 0; 22 for(;x>0;x-=(x&-x)) 23 ans += res[x]; 24 return ans; 25 } 26 int main() 27 { 28 int N,M,T; 29 cin>>T; 30 while(T--) 31 { 32 cin>>N; 33 memset(res,0,sizeof(res)); 34 memset(vis,0,sizeof(vis)); 35 for(int i=1;i<=N;i++) 36 { 37 scanf("%lld",&vec[i]); 38 add(i,vec[i]); 39 if(!vis[vec[i]]) vis[vec[i]] = i; 40 } 41 cin>>M; 42 for(int i=0;i<M;i++) 43 { 44 scanf("%d%d",&q1[i],&q[i].first); 45 q[i].second = i; 46 } 47 sort(q,q+M); 48 int right = 1; 49 for(int i=0;i<M;i++) 50 { 51 for(int j=right;j<=q[i].first;j++) 52 { 53 if(vis[vec[j]] != j) 54 { 55 add(vis[vec[j]],-vec[j]); 56 vis[vec[j]] = j; 57 } 58 } 59 right = q[i].first; 60 out[q[i].second] = sum(q[i].first) - sum(q1[q[i].second]-1); 61 } 62 for(int i=0;i<M;i++) 63 { 64 printf("%lld\n",out[i]); 65 } 66 } 67 return 0; 68 }