P1972 [SDOI2009] HH的项链(维护区间内不同数的个数)
大致题意给出一个范围n个,有多种贝壳(具体到每一个位置),给m个询问求某区间有多少种不同的贝壳
前置知识:简单的树状数组、结构体及cmp(compare)、离线处理思想
由于每一段询问范围和大小不同,不好处理,而此时开多个线段数组维护是不现实的(爆了)。这时候我们可以考虑,以右边界为基准进行排序,边进行线段树的维护边对区间进行求和,发现这是可行的:每一次对这个右移的位置进行操作的时候,都把某个数字出现的地方(最新遇到的一次)update为1,这样一定查询的是最接近的地方,即一定能查到(结合代码可以更好解释)
(此处图示样例源于董晓老师的博客)
然后再将每一段的结果存进一个数组(前缀和),在最后进行统一输出,这样就运用到了离线处理的思想。
下面贴代码
#include<iostream>
#include<algorithm>
using namespace std;
const int N=1e6+10;
int n;
struct node{
int l,r,id;
}nodes[N];
int tree[N];
int last[N];//记录数字最后出现在什么位置
int ans[N];//用于离线处理的记录,统一输出
int lowbit(int x){
return x & -x;
}
void update(int x,int k){
while(x<=n){
tree[x]+=k;
x+=lowbit(x);
}
}
int sum(int x){ //以上三个函数是简单的树状数组实现
int ans=0;
while(x){
ans+=tree[x];
x-=lowbit(x);
}
return ans;
}
bool cmp(node x,node y){ //结构体的比较,以每一段的右边进行排序
return x.r<y.r;
}
int a[N];
int main(){
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
cin>>n;
for(int i=1;i<=n;++i) cin>>a[i];
int m;cin>>m;
for(int i=1;i<=m;++i){
cin>>nodes[i].l>>nodes[i].r;
nodes[i].id=i;//记录一下位置,最后好统一输出
}
sort(nodes+1,nodes+1+m,cmp);
for(int i=1,now=1;i<=m;++i){
for(int j=now;j<=nodes[i].r;++j){//now用于记录现在更新到了哪一个节点了
if(last[a[j]]) update(last[a[j]],-1);
//初始化是0,如果存在就去掉先前出现的,即-1
update(j,1);
last[a[j]]=j;//记录最后一次出现的位置
}
now=nodes[i].r+1;//记得要+1
ans[nodes[i].id]=sum(nodes[i].r)-sum(nodes[i].l-1);
}
for(int i=1;i<=m;++i) cout<<ans[i]<<endl;
return 0;
}