P1972 [SDOI2009] HH的项链(可持续化线段树)

题目描述
HH 有一串由各种漂亮的贝壳组成的项链。HH 相信不同的贝壳会带来好运,所以每次散步完后,他都会随意取出一段贝壳,思考它们所表达的含义。HH 不断地收集新的贝壳,因此,他的项链变得越来越长。

有一天,他突然提出了一个问题:某一段贝壳中,包含了多少种不同的贝壳?这个问题很难回答…… 因为项链实在是太长了。于是,他只好求助睿智的你,来解决这个问题。

输入格式
一行一个正整数 n,表示项链长度。
第二行 n 个正整数 ai​,表示项链中第 i 个贝壳的种类。

第三行一个整数 m,表示 HH 询问的个数。
接下来 m 行,每行两个整数 l,r,表示询问的区间。

输出格式
输出 m 行,每行一个整数,依次表示询问对应的答案。

输入输出样例
输入 #1复制

6
1 2 3 4 3 5
3
1 2
3 5
2 6
输出 #1复制

2
2
4
解析:

我们在记录离线做法上(P1972 [SDOI2009] HH的项链(线段树+离线做法+排序)-CSDN博客),进行改进创建n棵线段树即可。

用 一个 rt记录其删掉上次重复的 个数,在赋值到 当前 第 i棵 线段树上。

代码如下 :

// 线段树+离线处理查询
#include <iostream>
#include <cstring>
#include <algorithm>
#include <vector>
using namespace std;

const int N = 1000005;
#define mid ((l+r)>>1)
int n,m,a[N],last[N];
int root[N],tot;
int ls[N*40],rs[N*40],sum[N*40];

void change(int &u,int v,int l,int r,int p,int k)
{
	u = ++tot;
	ls[u] = ls[v];
	rs[u] = rs[v];
	sum[u] = sum[v]+k;
	if(l == r) return;
	if(p <= mid) 
		change(ls[u],ls[v],l,mid,p,k);
	else{
		change(rs[u],rs[v],mid+1,r,p,k);
	}
} 

int query(int u,int l,int r,int p)
{
	if(l==r) return sum[u];
	if(p <= mid){
		return query(ls[u],l,mid,p) + sum[rs[u]];
	}
	else return query(rs[u],mid+1,r,p);
}

int main()
{
	scanf("%d",&n);
	for(int i = 1;i<= n;i++){
		scanf("%d",a+i);
	}
	
	for(int i = 1,rt;i <= n;i++)
	{
		if(!last[a[i]]) change(root[i],root[i-1],1,n,i,1); //没有出现过 直接 第 i个点 设置为1 
		else {
			change(rt,root[i-1],1,n,last[a[i]],-1);
			change(root[i],rt,1,n,i,1);
		}
		last[a[i]] = i; //记录了a[i]最后一个出现的位置  
	}
	
	scanf("%d",&m);
	int l,r;
	while(m--)
	{
		scanf("%d%d",&l,&r);
		printf("%d\n",query(root[r],1,n,l));//第r个版本的 左端点为 l的总和 
	}
	return 0;
}

时间复杂度为:O(n*logn)

  • 6
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值