为什么会变成这样呢? #3(并查集维护区间)

文章讨论了如何在给定字符串(S)和区间(T)的情况下,通过重新排列字符顺序,使(T)的字典序最大化的算法。关键策略是利用重要度和并查集合并规则,结合计数排序,实现近似(O(n))的期望复杂度,以解决CF1847D问题为例进行说明。

给定长度为 \(n\) 的字符串 \(S\) 以及 \(m\) 个区间 \([l_i, r_i]\),记 \(T=S[l_1,r_1]+\cdots+S[l_m,r_m]\),其中 \(S[x,y]\) 表示从第 \(x\) 个字符到第 \(y\) 个字符的子串。求如何重新排列 \(S\) 中字符的顺序使得 \(T\) 的字典序尽可能大。

期望复杂度: 近似 \(O(n)\)

czy's trick

\(S\) 中的每个位置有一个重要度,例如位置 \(l_1\) 是最重要的,应当放 \(S\) 中最大的字符,其次是 \(l_1+1,\dots,r_1,l_2,\dots\) 这些位置,且区间重复的部分以第一次出现为准。

相当于原问题变为了一个区间覆盖问题,我们只要以较快的速度跳过已经覆盖的区间即可。我们可以使用并查集维护区间,每个点对应并查集中的元素,在并查集合并的时候总是以较大的数作为父亲,则一个点的代表元就是其所在区间的右端点。

结合计数排序即可做到 \(O(n\alpha(n))\) 的时间复杂度。

例题:

### 使用线段树解决区间加操作后求历史上每个区间的零元素个数之和 #### 线段树结构设计 为了高效处理区间加法以及查询历史上的零元素个数,可以在线段树节点中存储如下信息: - `min_value`:当前区间内的最小值。 - `frequency_of_min`:最小值出现的次数。 - `zero_count`:当前区间内等于0的元素数目。 - `lazy_tag`:懒惰标记用于延迟更新。 这些属性允许快速执行区间修改(增加)和查询操作。当某个区间的最小值变为正数时,则该区间不再含有任何零元素;反之亦然[^1]。 #### 更新策略 对于每次范围增量操作,在向下传递之前先应用懒惰标签到子节点上,并清除父级节点的懒惰状态。这确保了只有真正受影响的部分才会被重新评估其内部特性变化情况。具体来说就是调整各个叶子结点对应的数组元素之后再自底向上逐层刷新各层次的信息汇总直至根部完成整个过程中的所有必要变更记录工作。 #### 查询逻辑 要获取某段时间序列里曾经存在过的全部为零的位置总数目,可以通过比较不同版本间差异来间接得出结论——即利用两个时间戳所对应的状态快照做减法运算获得新增/消失了多少个这样的特殊状况实例即可达成目标需求。 以下是Python实现的一个简化版例子展示上述概念的应用方式: ```python class SegmentTreeNode: def __init__(self, start, end): self.start = start self.end = end self.min_value = float(&#39;inf&#39;) self.frequency_of_min = 0 self.zero_count = 0 self.lazy_tag = 0 self.left_child = None self.right_child = None def build_tree(node, array): if node.start == node.end: node.min_value = array[node.start] node.zero_count = int(array[node.start] == 0) else: mid = (node.start + node.end) // 2 node.left_child = SegmentTreeNode(node.start, mid) node.right_child = SegmentTreeNode(mid + 1, node.end) build_tree(node.left_child, array) build_tree(node.right_child, array) update_node_info(node) def add_range_update(root, l, r, value): if root is None or l > r: return push_down(root) if root.start >= l and root.end <= r: apply_lazy_tag(root, value) elif not (root.end < l or root.start > r): add_range_update(root.left_child, l, r, value) add_range_update(root.right_child, l, r, value) update_node_info(root) def get_zero_counts_history(current_state_root, previous_state_root=None): result = current_state_root.zero_count if previous_state_root: result -= previous_state_root.zero_count return max(0, result) def apply_lazy_tag(node, delta): node.min_value += delta if node.min_value == 0: node.zero_count = node.frequency_of_min elif node.min_value > 0: node.zero_count = 0 node.lazy_tag += delta def push_down(node): if node.lazy_tag != 0: children = [child for child in (node.left_child, node.right_child) if child] for child in children: apply_lazy_tag(child, node.lazy_tag) node.lazy_tag = 0 def update_node_info(node): min_left = node.left_child.min_value if node.left_child else float(&#39;inf&#39;) min_right = node.right_child.min_value if node.right_child else float(&#39;inf&#39;) new_min = min(min_left, min_right) freq_sum = sum( [ getattr(child, &#39;frequency_of_min&#39;, 0) for child in (node.left_child, node.right_child) if getattr(child, &#39;min_value&#39;, float(&#39;inf&#39;)) == new_min ] ) zero_cnt = ( node.left_child.zero_count if node.left_child else 0 + node.right_child.zero_count if node.right_child else 0 ) node.min_value = new_min node.frequency_of_min = freq_sum node.zero_count = zero_cnt ``` 此代码片段展示了如何构建一棵支持动态区间增益及统计过去一段时间内曾达到过特定条件的数据量的线段树。通过这种方法可以在O(log n)时间内有效地管理大规模数据集上的复杂查询请求。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值