<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>权值线段树 on Zirnc's Blog</title><link>https://blog.chungzh.cn/blog/%E6%9D%83%E5%80%BC%E7%BA%BF%E6%AE%B5%E6%A0%91/</link><description>Recent content in 权值线段树 on Zirnc's Blog</description><generator>Hugo -- gohugo.io</generator><language>zh-cn</language><lastBuildDate>Sun, 14 Aug 2022 00:00:00 +0000</lastBuildDate><atom:link href="https://blog.chungzh.cn/blog/%E6%9D%83%E5%80%BC%E7%BA%BF%E6%AE%B5%E6%A0%91/index.xml" rel="self" type="application/rss+xml"/><item><title>Luogu-P3521 「POI2011」ROT-Tree Rotations</title><link>https://blog.chungzh.cn/oi-history/luogu-p3521/</link><pubDate>Sun, 14 Aug 2022 00:00:00 +0000</pubDate><guid>https://blog.chungzh.cn/oi-history/luogu-p3521/</guid><description>题意 给定一颗有 $n$ 个叶节点的二叉树。每个叶节点都有一个权值 $p_i$（注意，根不是叶节点），所有叶节点的权值构成了一个 $1 \sim n$ 的排列。
对于这棵二叉树的任何一个结点，保证其要么是叶节点，要么左右两个孩子都存在。
现在你可以任选一些节点，交换这些节点的左右子树。
在最终的树上，按照先序遍历遍历整棵树并依次写下遇到的叶结点的权值构成一个长度为 $n$ 的排列，你需要最小化这个排列的逆序对数。
$2 \leq n \leq 2 \times 10^5$， $0 \leq x \leq n$，所有叶节点的权值是一个 $1 \sim n$ 的排列。
分析 按照先序遍历整棵树，取叶结点，用人话说就是从左到右取叶子结点。
重要性质：交换了一个点的左右子树之后，不会影响左子树内和右子树内的逆序对数量。
考虑一个任意的结点，对它的子树中叶子的逆序对进行分类讨论：
都在左子树内； 都在右子树内； 跨越左右子树。 交换左右子树之后，受到影响的显然只有第三种情况。第一、第二种情况分治下去就可以转化成第三种再计算。
如何计算答案呢？一开始，对于每一个叶子结点，我们都建立一棵权值线段树（动态开点）并记录 $p_i$ 出现了 $1$ 次。合并 $r1, r2$ 时，逆序对的个数就是 $tree[rc[r1]] \times tree[lc[r2]]$。因为 $r1, r2$ 对应的权值区间 $[L, R]$ 是相同的，而 $lc[r1], lc[r2]$ 对应的权值区间就是 $[L, M]$， $rc[r1], rc[r2]$ 对应的权值区间是 $[M+1, R]$，$rc[]$ 中记录的数都比 $lc[]$ 中的要大，而 $r1$ 对应的数的编号小于 $r2$（也就是 $r1$ 是左子树，$r2$ 是右子树），满足逆序对的条件。</description></item><item><title>可持久化线段树笔记</title><link>https://blog.chungzh.cn/oi-history/persistent-seg-tree/</link><pubDate>Sun, 14 Aug 2022 00:00:00 +0000</pubDate><guid>https://blog.chungzh.cn/oi-history/persistent-seg-tree/</guid><description>动态开点线段树 常规写法的线段树只能维护不算很长的数组，由于空间不够，对于 $10^9$ 级别的数组却不能很好地维护。所以，我们要用到动态开点线段树。
核心思想：节点只有在有需要的时候才被创建。
比如说，要求在一个长度为 $n &amp;lt; 10^9$ 的数组上实现区间求和、单点修改的操作，初始数组元素值均为 0。
那么，我们一开始只创建一个根结点，接下来遵循动态开点的核心思想进行操作。
比如下面这张图的例子，我们依次修改 1, 2, 8 三个结点，途中创建了必要的结点。而在图中没有显示的空结点并没有被创建，视为 0，这样就节省了空间。
那么对于区间修改时，会有 pushdown() 操作，可能会修改一个不存在的结点。这时有两个解决方案：
在 pushdown() 时，如果缺少孩子，就直接创建一个新的孩子就可以了。 使用 标记永久化 技巧（李超线段树），让结点不再进行 pushdown()，进一步节省了空间。 复杂度分析：单次操作的时间复杂度是不变的，为 $O(\log n)$。对于空间复杂度，由于每次操作都有可能创建并访问全新的一系列结点，因此 $m$ 次操作的空间复杂度是 $O(m\log n)$，不再是原本线段树的 $O(n)$。
代码实现：
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 int n, cnt, root; // cnt 表示当前结点个数 int sum[N*2], ls[N*2], rs[N*2]; void upd(int&amp;amp; rt, int l, int r, int p, int f) { // 注意这里传入一个引用，可以修改 ls 或 rs 数组 if (!</description></item></channel></rss>