ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

洛谷P1972HH的项链 题解

2022-08-17 09:02:46  阅读:149  来源: 互联网

标签:洛谷 贝壳 P1972HH int 题解 元素 贡献 项链 区间


题目描述

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

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

题目分析

这个问题是一个区间求值的问题,数据范围辣么大,自然而然的想到利用前缀和的思想

BUT 这道题并不是简单的区间和,而是区间内所有数值的种类数

也就是说,一段区间内重复的元素只有其中一个对答案的贡献为 \(1\),其他所有都不做贡献

那么我们就从每种相同的元素选出一个代表,代表做出 \(1\) 的贡献,而其他元素都不做贡献不就行了?
(说起来容易,到底怎么做呀!)

那我们到底选 哪一位幸运观众 哪一个数来当我们的代表元素哩?

是第一个?第二个?最后一个?还是都可以?

答案是:(刮刮乐——>)最后一个

为什么是最后一个呢?

别急,听我仔细向您道来~

如果我们不选一段区间里的重复元素中的最后一个作为代表元素

那么,如果正好有一段区间,只包含这些元素中的最后那一个,答案本应该加上 \(1\),但我们却加不上,因为那 \(1\) 的贡献在别人身上

所以我们应该把所有重复元素贡献集中到最后一个
当然不是全部的最后一个,是会随着从左到右考虑而变化的

之后,我们就有了如下思路:

  • 处理输入

  • 预处理出对于数列中的每一个元素 \(x\),上一个 \(x\) 出现的位置

  • 离线存储所有询问,并按右端点从小到大排序

  • 从 \(1\) 到 \(n\) 枚举数列中的每一数,把上一次出现他的位置的贡献置为 \(0\)(-1),并把当前位置贡献置为 \(1\)(+1)

  • 用简单好写常数小空间不大的树状数组维护区间和

  • 如果当前位置是某些查询的右端点,就查询一下贡献数列的区间 \([l_i,r_i]\) 的和作为这次询问的答案

  • 输出答案

(输入量巨大,记得加快读快出)

思路分析完毕,上代码:

AC代码

#include<iostream>
#include<algorithm>

using namespace std;

const int N=1e6+10;

int n,m;
int c[N];
int l[N],o[N];
int a[N];

void read(int& x)
{
	x=0;
	int f=1;
	char c;
	c=getchar();
	while(c<'0'||c>'9')
	{
		if(c=='-')
			f=-1;
		c=getchar();
	}
	while(c>='0'&&c<='9')
	{
		x=x*10+c-'0';
		c=getchar();
	}
	x*=f;
}

void write(int x)
{
	if(x<0)
	{
		putchar('-');
		x=-x;
	}
	if(x>9)
		write(x/10);
	putchar(x%10+'0');
}

struct Q{
	int l,r;
	int id;
}q[N];

int lowbit(int x)
{
	return x&-x;
}

void update(int x,int k)
{
	while(x<=n)
	{
		c[x]+=k;
		x+=lowbit(x);
	}
}

int query(int x)
{
	int res=0;
	while(x)
	{
		res+=c[x];
		x-=lowbit(x);
	}
	return res;
}

bool cmp(Q A,Q B)
{
	return A.r<B.r;
}

int main()
{
	read(n);
	for(int i=1;i<=n;i++)
	{
		int x;
		read(x);
		o[i]=l[x];
		l[x]=i;
	}
	read(m);
	for(int i=1;i<=m;i++)
	{
		read(q[i].l),read(q[i].r);
		q[i].id=i;
	}
	sort(q+1,q+m+1,cmp);
	int p=1;
	for(int i=1;i<=n;i++)
	{
		if(o[i])
			update(o[i],-1);
		update(i,1);
		while(i==q[p].r)
		{
			a[q[p].id]=query(q[p].r)-query(q[p].l-1);
			p++;
		}
	}
	for(int i=1;i<=m;i++)
		write(a[i]),puts("");
	
	return 0;
}

完结撒花?

不,还没有完全结束

我们刚才证明了把贡献放在除了最后一个的其他位置不能得到这道题的答案

那他能得到什么题的答案呢?

刚才把代表元素定位最后一个,就可以得到区间内出现次数大于等于一的数的种类

那如果我们选倒数第二个,是不是就可以得到区间内出现次数大于等于二的数的种类

聪明!当然可以!

补充完这一点,这题就彻底做完了

白白ヾ(•ω•`)o~~~

标签:洛谷,贝壳,P1972HH,int,题解,元素,贡献,项链,区间
来源: https://www.cnblogs.com/Orange-Star/p/16593354.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有