离线处理在树状数组中的应用(P1972 HH的项链)

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;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值