首页 > 其他分享 >洛谷P1972HH的项链 题解

洛谷P1972HH的项链 题解

时间:2022-08-17 08:55:37浏览次数:88  
标签:洛谷 贝壳 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,题解,元素,贡献,项链,区间
From: https://www.cnblogs.com/Orange-Star/p/16593354.html

相关文章

  • LeetCode 螺旋矩阵 II 算法题解 All In One
    LeetCode螺旋矩阵II算法题解AllInOnejs/ts生成螺旋矩阵螺旋矩阵原理图解动态赋值arr[i]//动态更新indexleti=0;while(left<=right&&t......
  • ARC094D题解
    设\(A<B\),\(C=\max(\sqrt{AB-1},A)\),答案为:\[C-1+\frac{AB-1}{C+1}\]如果\(A>B\)时显然可以互换,接下来称\(A\)所在的比赛为第一场比赛,\(B\)所在的比赛为第二场比赛......
  • 题解 [ZJOI2010]排列计数
    好题。%你赛考到了不会摆烂,后来发现原来有向下取整,题面没有。。。(就算有我也做不出来啦qAq首先我们会发现这个长得就是小根堆,答案就变成了小根堆的计数。首先最小的......
  • ubuntu16.04中文乱码问题解决
    1、先输入locale-a,查看一下现在已安装的语言2、若不存在如zh_CN之类的语言包,进行中文语言包装:apt-getinstalllanguage-pack-zh-hans3、安装好后我们可以进行临时修......
  • 洛谷 P6242 【模板】线段树 3 吉司机线段树 区间取最小值 维护历史最大值和区间和
    题目背景本题是线段树维护区间最值操作与区间历史最值的模板。题目描述给出一个长度为 nn 的数列 AA,同时定义一个辅助数组 BB,BB 开始与 AA 完全相同。接下来......
  • LeetCode 反转链表算法题解 All In One
    LeetCode反转链表算法题解AllInOnejs/ts实现反转链表反转链表原理图解双指针,swap交换//反转双指针//swap:a=b;c=a;b=c;letprev:List......
  • 洛谷 P3964 松鼠聚会
    前言由于未知原因,解密被取消了。其实就是我懒$\color{white}{恭喜你发现本彩蛋!Name:UR\next\\\Password:Zjd6MWpnMjZhNA==空格\to下划线}$正文题面有......
  • 洛谷P1714切蛋糕题解
    P1714切蛋糕题目描述今天是小Z的生日,同学们为他带来了一块蛋糕。这块蛋糕是一个长方体,被用不同色彩分成了\(n\)个相同的小块,每小块都有对应的幸运值。小Z作......
  • 【题解】【Shopping】【树上依赖型背包】
    很厉害的题。在任务计划里躺了一年。。。题目传送门思考之路题目要让我们在付出不超过m的代价在树上找到一个权值最大的连通块。容易想到树形dp,设\(f_{i,j}\)表示选的......
  • 洛谷P2622 关灯问题II引发的关于DP实现形式及后效性的思考
      动态规划要求已经求解的子问题不受后续阶段的影响,即无后效性。而在这种递推的实现方式中,后面枚举的状态可能更新前面已经枚举过的状态。也就是说,这种递推的实现方式是......