题目链接:
https://vjudge.z180.cn/problem/HDU-3333
题目大意:给你长度为N的序列,再给你Q次询问,每次询问给你一个区间 [ l , r ] , 按 询 问 顺 序 依 次 输 出 序 列 中 区 间 [ l , r ] 的 数 的 总 和 ( 重 复 值 只 记 录 一 次 ) [l,r],按询问顺序依次输出序列中区间[l,r]的数的总和(重复值只记录一次) [l,r],按询问顺序依次输出序列中区间[l,r]的数的总和(重复值只记录一次)(这Q次询问是一次性给出来的,这也是这道题可以离线处理的原因).
分析:这道题如果不要求重复值只记录一次,那么就直接裸的线段树求区间和的问题,但是加了一个条件之后难度就上升了,我们想如果一个数字x在 [ l , r ] [l,r] [l,r]这个区间多次出现,我们只把它最后一次出现的位置赋值为x即可,之前出现的位置都赋值为0,就可以满足要求了,所以我们用线段树去维护下标对应的值即可(单点修改),求区间总和就是直接(区间查询),但是这道题给你Q个区间,肯定要先处理区间靠前的也就是r小的,如果不按这样的顺序处理,那么就会乱套,例如:x在询问区间[1,3]出现了一次,而在询问区间[4,5]也出现了一次,那么如果我们先计算[4,5]这个区间的和,那么x最后出现的位置是不是就在区间[4,5],这时候再去计算区间[1,3],x最后出现的下标就不在区间[1,3]内了,也就是x这个值没有被计算进[1,3]的总和,所以答案出错.
AC代码:
#include<bits/stdc++.h>
using namespace std;
const int Maxn = 3e4+10;
const int Q = 1e5+10;
typedef long long ll;
ll sum[Maxn<<2];
/*单点跟新*/
void pushup(int dep)
{
sum[dep] = sum[dep<<1]+sum[dep<<1|1];
return ;
}
void update(int pos,int val,int dep,int l,int r)
{
if(l==r)
{
sum[dep] = val;//叶子节点的这一步就想当于pushup(dep)
return ;
}
int mid = l+r>>1;
if(pos<=mid) update(pos,val,dep<<1,l,mid);
else update(pos,val,dep<<1|1,mid+1,r);
pushup(dep);//跟新完儿子节点之后,再回溯上来跟新父节点的值
return ;
}
ll query(int ql,int qr,int dep,int l,int r)
{
if(ql<=l&&r<=qr)
{
return sum[dep];
}
ll ans = 0;
int mid = l+r>>1;
if(ql<=mid) ans+=query(ql,qr,dep<<1,l,mid);
if(qr>mid) ans+=query(ql,qr,dep<<1|1,mid+1,r);
return ans;
}
struct Node
{
int l,r,pos;
}node[Q];
int cmp(struct Node a,struct Node b)
{
return a.r<b.r;
}
int vis[Maxn];
int a[Maxn];
int tmp[Maxn];
int pre[Maxn];//记录A[i]这个数上一次出现的位置
ll ans[Q];
int main()
{
int t;
cin>>t;
while(t--)
{
memset(vis,0,sizeof(vis));
memset(a,0,sizeof(a));
memset(tmp,0,sizeof(tmp));
memset(pre,0,sizeof(pre));
memset(ans,0,sizeof(ans));
int n;
cin>>n;
for(int i=1;i<=n;i++)
{
cin>>a[i];
tmp[i] = a[i];
}
sort(tmp+1,tmp+n+1);
int m;//m次询问
cin>>m;
for(int i=1;i<=m;i++)
{
cin>>node[i].l>>node[i].r;
node[i].pos = i;
}
sort(node+1,node+m+1,cmp);
for(int i=1,j=1;i<=n;i++)
{
int pos = lower_bound(tmp+1,tmp+n+1,a[i])-tmp;
// cout<<pos<<"yy"<<'\n';
if(!vis[pos])//没有出现过
{
update(i,a[i],1,1,n);
vis[pos] = 1;
pre[pos] = i;
}
else
{
/*跟新位置*/
update(pre[pos],0,1,1,n);//以前的位置已不再是最新,直接置0
update(i,a[i],1,1,n);
pre[pos] = i;
}
for(;j<=m;j++)
{
/*i必须跟新到node[j].r了才能得到有[node[j].l,node[j].r]的和准确值*/
if(i==node[j].r) ans[node[j].pos] = query(node[j].l,node[j].r,1,1,n);
else break;
}
}
for(int i=1;i<=m;i++) printf("%lld\n",ans[i]);
}
}