题意:求一个序列中所有数字的和,其中数值相同的只能计算一次。
做法:可以先储存所有的请求,然后按照它们的右边界排序,在查询的同时更新区间。这里其实有一点点DP的味道,在它进行某个查询之前,保证所有的重复数字都被删除光了,并且有不能影响其他查询,所以呢,只能从最近的那个操作进行计算。1次query即可
#include<cstdio> #include<cstring> #include<algorithm> #define left l,m,x<<1 #define right m+1,r,x<<1|1 #define LL __int64 const int LMT=50005; using namespace std; int a[LMT],pre[1000003]; LL ans[200003],sum[LMT<<2]; struct line { int l,r,id; bool operator<(const line &y)const { return r<y.r; } }e[200003]; void init(void) { memset(pre,-1,sizeof(pre)); memset(sum,0,sizeof(sum)); } void update(int op,int pos,int l,int r,int x) { if(l==r) { sum[x]=op; return; } int m=(l+r)>>1; if(pos<=m)update(op,pos,left); if(pos>m)update(op,pos,right); sum[x]=sum[x<<1]+sum[x<<1|1]; } LL query(int L,int R,int l,int r,int x) { if(L<=l&&r<=R) return sum[x]; LL res=0; int m=(l+r)>>1; if(L<=m)res+=query(L,R,left); if(R>m)res+=query(L,R,right); return res; } int main(void) { int T,n,m,i,st; scanf("%d",&T); while(T--) { init(); scanf("%d",&n); for(i=0;i<n;i++) scanf("%d",&a[i]); scanf("%d",&m); for(i=0;i<m;i++) { scanf("%d%d",&e[i].l,&e[i].r); e[i].l--;e[i].r--; e[i].id=i; } sort(e,e+m); st=0; for(i=0;i<n&&st<m;i++) { if(pre[a[i]]!=-1)update(0,pre[a[i]],0,n-1,1); update(a[i],i,0,n-1,1); while(st<m&&e[st].r<=i) { if(e[st].r==i) ans[e[st].id]=query(e[st].l,e[st].r,0,n-1,1); st++; } pre[a[i]]=i; } for(i=0;i<m;i++)printf("%I64d\n",ans[i]); } return 0; }