http://acm.hdu.edu.cn/showproblem.php?pid=3333
题意:求区间不同数的和。
思路:主席树和树状数组的的做法就不介绍了,blog里有介绍过。。这里介绍一下分块的做法(本来是想去莽5919。。。结果没莽过。。。只能改改代码拿3333来练练手了)
这里维护每个块的值(最之前出现的位置)有序,之后就是分块套路,查询整块的时候去二分有序数组,就可以知道哪些是没有计算过的了。(显然小于L才是第一次出现)。。于是乎排序每个块之外再加一个每块的前缀和就好了。不多说看代码。。分块风格学的卿学姐的。感觉比以前自己写的清爽不少,毕竟内存不要钱嘛(误),总之很清爽233333
代码:
#include<bits/stdc++.h>
using namespace std;
const int MAXN = 30010;
int n,q,block,num,a[MAXN],belong[MAXN],L[MAXN],R[MAXN];
pair<int,int> c[MAXN],d[MAXN];
long long sum[MAXN],Q[MAXN*5];
map<int,int> mp;
int in(){
int res=0;char c;int f=1;
while((c=getchar())<'0' || c>'9')if(c=='-')f=-1;
while(c>='0' && c<='9')res=res*10+c-'0',c=getchar();
return res*f;
}
int build(int n){
block=sqrt(n);
num=n/block;
if(n%block!=0) num++;
for(int i=0;i<num;i++){
L[i]=i*block+1;
R[i]=(i+1)*block;
}
R[num-1]=n;
int cnt=0;
for(int i=1;i<=n;i++){
if(i<=R[cnt]) belong[i]=cnt;
else belong[i]=++cnt;
}
mp.clear();
for(int i = 1; i<= n; i++){
c[i]=make_pair(mp[a[i]],a[i]);
mp[a[i]] = i;
}
for(int i=1;i<=n;i++) d[i]=c[i];
for(int i=0;i<num;i++) sort(c+L[i],c+R[i]+1);
sum[0]=0;
for(int i=1;i<=n;i++){
if(belong[i]==belong[i-1]) sum[i]=sum[i-1]+c[i].second;
else sum[i]=c[i].second;
}
}
long long query1(int l,int r){
long long ret=0;
if(belong[l]==belong[r]){
for(int i=l;i<=r;i++)
if(d[i].first<l) ret+=d[i].second;
return ret;
}
for(int i=l;i<=R[belong[l]];i++)
if(d[i].first<l) ret+=d[i].second;
for(int i=belong[l]+1;i<belong[r];i++){
int x=(int)(lower_bound(c+L[i],c+R[i]+1,make_pair(l,-1))-c);
if(x!=L[i]) ret+=sum[x-1];
}
for(int i=L[belong[r]];i<=r;i++)
if(d[i].first<l) ret+=d[i].second;
return ret;
}
int main(){
Q[0]=0;
int t,cas=1;
scanf("%d",&t);
while(t--){
scanf("%d",&n);
for(int i = 1; i <= n; i++) a[i]=in();
build(n);
scanf("%d",&q);
int l,r;
for(int i=1;i<=q;i++){
l=in();r=in();
if(r<l) swap(l,r);
Q[i]=query1(l,r);
}
for(int i=1;i<=q;i++) printf("%I64d\n",Q[i]);
}
return 0;
}