期末上机之维护偏序关系系列问题

-题意

给定一个长度为n的序列,让你求满足:
i &lt; j i&lt;j i<j a [ i ] ∗ 2 + 1 ≤ a [ j ] a[i]*2+1 \leq a[j] a[i]2+1a[j] ( i , j ) (i,j) i,j的个数


-做法

题目所给的条件显然是一个维护一维偏序关系的题目,类似于逆序数对的求法,我们可以用类似于归并排序的做法来维护它。
归并排序的过程中,可以考虑一个数组左半部分对右半部分的贡献,我们将左半数组记为a,右半数组记为b,如果a,b都是单调的数组的话就有非常好的性质,因为b是右半边数组,所以b数组的元素下标肯定是大于a数组的
在这里插入图片描述

这里可以看到b数组下标为j的地方对应的 a [ i ] ∗ 2 + 1 ≤ b [ j ] a[i]*2+1\leq b[j] a[i]2+1b[j]对应的最大的i如图所示,因为a,b两个数组我们假定都是单调的,当j增大的时候我们就不用每次从头枚举i,我们从上一个j所对应的i枚举就行,因为当j增大的时候
在这里插入图片描述
必然是如下图所示,我们所满足条件的i必然是要增大的。根据这个思想我们可以设定一个指针遍历st,st一开始指向左边数组的左端点,然后通过枚举j的位置(j的位置从右边数组的左端点开始),如果st指针满足 a [ j ] &gt; = a [ s t ] ∗ 2 + 1 a[j] &gt;= a[st] * 2 + 1 a[j]>=a[st]2+1 s t &lt; = m i d st &lt;= mid st<=mid,我们就把st指针向右移动一格,知道不满足的时候,我们统计st和左端点的差,把这个差新加到我们的结果里,这个差就表明对于这个 j j j我们有多少个 i i i满足条件,这样下去,一次线性遍历我们就可以完成相关满足条件数据的统计,在完成这个过程以后,我们执行merge操作将两个有序数组合并,递归的不断执行下去就可以得到我们最后的答案。


-注意点

因为题中 n n n最大有1e6,我们结果可能会超过 i n t int int范围,需要用更大的 l o n g l o n g long long longlong变量


-核心代码

  1. 分治
void merge(int start, int end){
	if (start < end){
		int mid = (start + end) / 2;
		merge(start, mid);
		merge(mid + 1, end);
		mergeit(start, end);
	}
}
  1. 遍历数组统计个数
int st = start;
	int mid = (start + end) / 2;
	for (int j = mid + 1; j <= end; j++){
		while (a[j] >= a[st] * 2 + 1 && st <= mid)
			++st;
		ans += (st-start);
	}
  1. merge
int b1 = start, b2 = mid + 1, k = 0;
	ll *tmp = (ll*)malloc((n+1)*sizeof(ll));
	while (b1 <= mid&&b2 <= end){
		if (a[b1] < a[b2])
			tmp[k++] = a[b1++];
		else
			tmp[k++] = a[b2++];
	}
	while (b1 <= mid)
		tmp[k++] = a[b1++];
	while (b2 <= end)
		tmp[k++] = a[b2++];
	for (int i = 0; i < k; i++)
		a[start + i] = tmp[i];
	free(tmp);

-复杂度分析

  1. 遍历数组统计个数的时候前面已经提过是 O ( n ) O(n) O(n)的复杂度,因为用指针指向了 j j j所覆盖的最大 i i i的位置,不然每次从头开始遍历 i i i的复杂度是 O ( n 2 ) O(n^2) O(n2),这是我们无法接受的。
  2. merge操作合并两个有序数组的复杂度也是 O ( n ) O(n) O(n),这个课上宋老师已经讲过相应操作。
  3. 然后我们要对上述操作执行 l o g ( n ) log(n) log(n)次(分治思想,每次把一个大区间拆成两个小区间直到区间长度为2的时候开始合并)
  4. 所以总体复杂度为 O ( n l g n ) O(nlgn) O(nlgn)

-总结

核心部分还是用指针指向左端数组的元素使得我们能够线性遍历这个区间得到结果,这里大家可以思考一下如果题目改成 i &lt; j i&lt;j i<j a [ i ] ∗ 2 + 1 ≥ a [ j ] a[i]*2+1 \geq a[j] a[i]2+1a[j]我们该怎么处理这个指针呢?(和逆序数对的指针处理方式就基本一样了)


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
提供的源码资源涵盖了安卓应用、小程序、Python应用和Java应用等多个领域,每个领域都包含了丰富的实例和项目。这些源码都是基于各自平台的最新技术和标准编写,确保了在对应环境下能够无缝运行。同时,源码中配备了详细的注释和文档,帮助用户快速理解代码结构和实现逻辑。 适用人群: 这些源码资源特别适合大学生群体。无论你是计算机相关专业的学生,还是对其他领域编程感兴趣的学生,这些资源都能为你提供宝贵的学习和实践机会。通过学习和运行这些源码,你可以掌握各平台开发的基础知识,提升编程能力和项目实战经验。 使用场景及目标: 在学习阶段,你可以利用这些源码资源进行课程实践、课外项目或毕业设计。通过分析和运行源码,你将深入了解各平台开发的技术细节和最佳实践,逐步培养起自己的项目开发和问题解决能力。此外,在求职或创业过程中,具备跨平台开发能力的大学生将更具竞争力。 其他说明: 为了确保源码资源的可运行性和易用性,特别注意了以下几点:首先,每份源码都提供了详细的运行环境和依赖说明,确保用户能够轻松搭建起开发环境;其次,源码中的注释和文档都非常完善,方便用户快速上手和理解代码;最后,我会定期更新这些源码资源,以适应各平台技术的最新发展和市场需求。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值