ICode9

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

排列 题解

2022-06-29 21:33:15  阅读:119  来源: 互联网

标签:排列 cont 题解 operatorname 答案 序列 我们 bnd


题面

给定一个长度为4的排列a与一个长度为n的排列b。在b中选出长度为4的子序列使该子序列与排列a的相对顺序相同。输出选法个数。共24个subtask,意即所有排列都会出现。$ n \le 2000。 $

解法

我们考虑将这个排列a划分成两个互不相关的部分。两个部分互不相关,当且仅当他们在值域上不连续,且在位置上不连续。根据这个思路,我们可以定义“关键对”的概念。

若一个对为\((i, j)\),且 \(i < j\),其为关键对的充要条件是:\((i,j)\)、\((j,i)\)、\((a_i, a_j)\)、\((a_j, a_i)\) 不为 \((1,2)\),\((1,4)\),\((3,4)\) 中的任意一个。

这个条件是良定义的。当我们选取出一个关键对 \((i,j)\) 后,我们就可以通过在b排列上确定 \(i\) 与 \(j\) 所对应的位置来使余下的位置不连续。我们以序列 $ a = $ \(\{4,1,3,2\}\) 为例子来说明具体选取的正确性。

对于该序列,其的一个关键对是 \((2,3)\)。我们设选取子序列的第二个值位于 \(i\),第三个值位于 \(j\),则第一个值的位置位于 \([1, i-1]\) 之间,第四个值位于 \([j+1, n]\) 之间。并且,第一个值的值域位于 \([b_j+1, n]\) 之间,第四个值位于 \([1, b_i-1]\) 之间。而当我们选择 \((1,2)\) 等非关键对时,我们无法将值域与范围全部分离。因此关键对的正确性是显然的。

然而我们发现,序列\(\{2,4,1,3\}\)与\(\{3,1,4,2\}\)是无法找到关键对的。我们需要另一种特殊的解决方案。

我们可以发现,这两个序列是等价的,我们只需要在 \(a=\) \(\{3,1,4,2\}\) 的时候将a与b进行翻转,问题就可以转化为\(\{2,4,1,3\}\) 的求解。

这个序列是特殊的。我们可以看到,当第四个值确定时,第一与第三个值的值域就已经确定了。同样地,当第二个值确定时,第一与第三个值的范围也确定了。这启示我们进行第二与第四个值的枚举。

我们首先进行第三个值的枚举。设其位置为 \(i\),并定义 \(\operatorname{bound} = b_i\)。随后我们进行第四个值的枚举,设其位置为 \(j\),并设 \(k \in [1, j-1]\) 时 $ b_k$ 的全体为 \(\operatorname{front}\),\(k \in [j+1, i-1]\) 时 $ b_k$ 的全体为 \(\operatorname{back}\)。

考虑朴素的贡献度统计。我们首先将 \(\operatorname{back}\) 插入一个树状数组 \(\operatorname{BIT_b}\),并对于每个 \(\operatorname{front}\) 的值,在 \(\operatorname{BIT_b}\) 中查询。其结果设为 \(\operatorname{cont}\)。具体而言,\(\operatorname{cont}\) 代表不关心 \(b_j\) 的取值时目前的答案。当 \(b_j > \operatorname{bound}\) 时,令答案 \(+\operatorname{cont}\)。

以此方法,单次统计是 \(O(n \log n)\) 的复杂度,不可接受。然而,朴素的思路启发我们进行贡献度的优化转移。具体方法如下:

对于每次 \(j\) 向后移动一位,我们发现,实际的 \(\operatorname{cont}\) 只会受两个位置的影响:\(b_{j-1}\) 加入 \(\operatorname{front}\),以及 \(b_{j}\) 从 \(\operatorname{back}\) 中离开。我们发现,只需要保证 \(O(\log n)\) 地维护 \(\operatorname{cont}\),我们就可以在正确的复杂度下解决此题。

由于 \(b_{j}\) 离开了 \(\operatorname{back}\),因此其无法再为目前的答案贡献。因此,我们需要维护 \(\operatorname{front}\) 对应的树状数组 \(\operatorname{BIT_f}\),在其中查询 \(b_{j}\) 的后缀和(直到目前的\(\operatorname{bound}\))并在答案中减去该部分贡献。对于 \(b_{j-1}\),我们在 \(\operatorname{BIT_b}\) 中查询其对应前缀和并在答案中加入该部分贡献。需要注意的是,我们需要先删去 \(b_{j}\),再进行查询操作,最后加入 \(b_{j-1}\),这点较显然。最后,判断 \(b_{j}\) 与 \(\operatorname{bound}\) 的关系,累加答案。

放出这部分代码以便理解:

for (int i = 4; i <= n; i++) {
	int bnd = b[i], tmp = 0;
	// tmp: 记录不管4的时候目前情况下的答案 
	frt.clear(), bk.clear();
	frt.add(b[1], 1); 
	for (int j = 3; j < i; j++) bk.add(b[j], 1);
	// 初始化 
	if (b[1] < bnd) tmp += bk.qry(b[1] - 1);
	// 若前面新加入的数小于bnd 
	// 那就把他关于后面目前所有数的贡献加进去 
	if (b[2] > bnd) ans += tmp;
	// 如果贡献可用 就累加 
	for (int j = 3; j < i-1; j++) {
		bk.add(b[j], -1);
		if (b[j-1] < bnd) tmp += bk.qry(b[j-1]-1);
		// 若前面新加入的数小于bnd 
		// 那就把他关于后面目前所有数的贡献加进去 
		if (b[j] < bnd) tmp -= frt.qry(bnd-1) - frt.qry(b[j]);
		// 如果有一个为答案贡献的数离开了 那就从答案中删去它对答案贡献的量 即小于bnd但大于它的数字个数 
		if (b[j] > bnd) ans += tmp;
		// 此时满足排列的条件,可以累加答案 
		frt.add(b[j-1], 1);
	}
}

标签:排列,cont,题解,operatorname,答案,序列,我们,bnd
来源: https://www.cnblogs.com/joke3579/p/premtt.html

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

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

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

ICode9版权所有