几乎完全一样的题目(区间内不同数的个数): 传送门
题意: 给你n个数,q次查询,问这个区间内,区间内数的和的个数有多少种,每个不同的数只能取一次。
做法:
- 莫队也可做,但是了解到树状数组也可以进行离线操作。
- 思路就是,记录相同数字中上一个数字出现的位置。
按照查询区间的右端点排序,从小到大访问右端点,更新当前位置时,如果之前有出现过相同的数字,相同数字所在的位置处更新为-a[i],否则更新为a[i]。 - 存储答案的方式类似莫队
AC代码:
#include<bits/stdc++.h>
using namespace std;
#define IO ios_base::sync_with_stdio(0),cin.tie(0),cout.tie(0)
#define pb(x) push_back(x)
#define sz(x) (int)(x).size()
#define sc(x) scanf("%d",&x)
#define abs(x) ((x)<0 ? -(x) : x)
#define all(x) x.begin(),x.end()
#define mk(x,y) make_pair(x,y)
#define fin freopen("in.txt","r",stdin)
#define fout freopen("out.txt","w",stdout)
typedef long long ll;
const int mod = 1e9+7;
const double PI = 4*atan(1.0);
const int maxm = 1e8+5;
const int maxn = 1e6+5;
const int INF = 0x3f3f3f3f;
const ll LINF = 1ll<<62;
struct node
{
int l,r;
int id;
}q[maxn];
int n,m;
int a[maxn],pre[maxn],tmp[maxn];
ll ans[maxn],c[maxn];
inline int read()
{
char x;
int u,flag = 0;
while(x = getchar(),x<'0' || x>'9') if(x == '-') flag = 1;
u = x-'0';
while(x = getchar(),x>='0' && x<='9') u = (u<<3)+(u<<1)+x-'0';
if(flag) u = -u;
return u;
}
inline int lowbit(int x){return x&(-x);}
inline void update(int x,int val)
{
for(int i=x;i<=n;i+=lowbit(i)) c[i]+=val;
}
inline ll query(int x)
{
ll res = 0;
for(int i=x;i;i-=lowbit(i)) res+=c[i];
return res;
}
inline ll range_query(int l,int r)
{
return query(r)-query(l-1);
}
bool cmp(node a,node b){
if(a.r == b.r) return a.l<b.l;
else return a.r<b.r;
}
int main()
{
// fin;
// IO;
int t = read();
while(t--)
{
memset(tmp,0,sizeof(tmp));
memset(c,0,sizeof(c));
n = read();
for(int i=1;i<=n;i++){
a[i] = read();
pre[i] = tmp[a[i]];
tmp[a[i]] = i;
}
m = read();
for(int i=1;i<=m;i++){
q[i].l = read();q[i].r = read();
q[i].id = i;
}
sort(q+1,q+1+m,cmp);
int cnt = 1;
for(int i=1;i<=m;i++)
{
for(;cnt<=q[i].r;cnt++)
{
if(pre[cnt]) update(pre[cnt],-a[cnt]);
update(cnt,a[cnt]);
}
ans[q[i].id] = range_query(q[i].l,q[i].r);
}
for(int i=1;i<=m;i++) printf("%lld\n",ans[i]);
}
return 0;
}