题目大意:
已知一串数字an,有m个询问,每个询问包含一个区间[a,b],问你an在区间[a,b]中有多少个不同的数。
解题思路:
我们把每个询问的右区间进行排序,每次我们对树状数组进行更新,怎么更新呢?对于重复出现的数字,我们只记录最后一次出现位置,并在对应位置打1(注意:这里不是对树状数组打1,树状数组本质上是对一个普通数组求和的维护,所以我们这里是对普通数组对应位置打1),之前打过1的位置清零。其它没出现过的数字就放0好了。什么时候更新呢?每次当询问区间的右边界往右移动的时候就开始更新,看是否有重复的数字或者第一次出现的数字。当询问区间排好序,并且询问的时候,我们是不用关心重复出现的靠左边的数字,这样做的好处是:我们将不同的数字,可以转化为区间求和问题!比如问[1,3]不同的数字,我们就可以认为是对[1,3]区间进行求和!
废话:
(1)这里使用了对询问排序这种预处理的思想可以学习。
(2)这种转换为对区间求和的思想可以学习。
#include <bits/stdc++.h>
#define LSOne(n) n&(-n)
using namespace std;
vector<pair<int,int>> range;
vector<pair<int,int>> rgh_no;
const int MAXN=1e6+10;
int flag[MAXN];
int ansarr[MAXN];
class fenwickedTree{
private:
vector<int> ft;
public:
fenwickedTree(int n){
ft.assign(n+1,0);
}
void adjust(int idx,int val){
for(;idx<(int)ft.size();idx+=LSOne(idx)){
ft[idx]+=val;
}
}
int getsum(int a,int b){
return a==1?getsumuntill(b):getsumuntill(b)-getsumuntill(a-1);
}
int getsumuntill(int a){
int sum=0;
for(;a;a-=LSOne(a)){
sum+=ft[a];
}
return sum;
}
};
int main(){
//ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
memset(flag,-1,sizeof(flag));
int n;scanf("%d",&n);
vector<int> neck;
fenwickedTree ft(n);
for(int i=0;i<n;i++){
int t;scanf("%d",&t);
neck.push_back(t);
}
scanf("%d",&n);
for(int i=0;i<n;i++){
int a,b;scanf("%d %d",&a,&b);
range.push_back({a,b});
rgh_no.push_back({b,i});
}
sort(rgh_no.begin(),rgh_no.end(),less<pair<int,int>>());
int poi=0;
for(int i=0;i<(int)rgh_no.size();i++){
if(rgh_no[i].first!=poi){
for(int lspoi=poi+1;lspoi<=rgh_no[i].first;lspoi++){
if(flag[neck[lspoi-1]]==-1){
flag[neck[lspoi-1]]=lspoi;
ft.adjust(lspoi,1);
}else{
ft.adjust(flag[neck[lspoi-1]],-1);
ft.adjust(lspoi,1);
flag[neck[lspoi-1]]=lspoi;
}
}
poi=rgh_no[i].first;
}
int lf=range[rgh_no[i].second].first;
int rg=range[rgh_no[i].second].second;
int ans=ft.getsum(lf,rg);
ansarr[rgh_no[i].second]=ans;
}
for(int i=0;i<(int)rgh_no.size();i++){
printf("%d\n",ansarr[i]);
}
return 0;
}