計算區間不同數的和(離線+樹狀數組)
題目傳送門:牛客練習賽52-B:Galahad
題意:
給一個長度爲n的數組,有q次詢問,每次詢問一個區間 ,問這個區間的和,但如果某一個數在這個區間出現了多次,這個數只能被計算一次。
思路:
題目中只有修改操作,所以我們可以離線處理。
我們讓所有查詢按右端點從小到大排序。對於每個區間將未添加的原數組元素設爲,如果 在前面已經出現過,那就讓前面的刪除。讓p這個位置的值變爲。最後對於這個區間求區間合即可。
這樣的話區間中出現相同的數只有最後出現位置纔有貢獻。且有這個數必可以計算出貢獻。
#include<bits/stdc++.h>
#define mset(a,b) memset(a,b,sizeof(a))
using namespace std;
typedef long long ll;
typedef pair<int,int> P;
const int N=5e5+20;
const int mod=1e9+7;
int vis[N];
struct Seqment
{
int l,r,id;
bool operator < (const Seqment & o) const
{
return r<o.r;
}
} seq[N];
struct TreeArray
{
int MXN;
ll bt[N];
int lowbit(int k)
{
return k&-k;
}
void modify(int k,ll val)
{
while(k<=MXN)
{
bt[k]+=val;
k+=lowbit(k);
}
}
ll getpre(int k)
{
ll sum=0;
while(k)
{
sum+=bt[k];
k-=lowbit(k);
}
return sum;
}
ll getsum(int l,int r)
{
return getpre(r)-getpre(l-1);
}
}solve;
ll w[N],res[N];
int main()
{
// ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
int n,m;
scanf("%d%d",&n,&m);
solve.MXN=n;
for(int i=1;i<=n;++i){
scanf("%lld",&w[i]);
// cin>>w[i];
}
for(int i=1;i<=m;++i){
scanf("%d%d",&seq[i].l,&seq[i].r);
seq[i].id=i;
}
sort(seq+1,seq+m+1);
int p=0;
for(int i=1;i<=m;++i)
{
while(p+1<=seq[i].r){
++p;
if(vis[w[p]]!=0){
solve.modify(vis[w[p]],-w[p]);
}
vis[w[p]]=p;
solve.modify(p,w[p]);
}
res[seq[i].id]=solve.getsum(seq[i].l,seq[i].r);
}
for(int i=1;i<=m;++i){
printf("%lld\n",res[i]);
}
return 0;
}