数据结构选讲

数据结构选讲

ABC282Ex

题意

给定两个长度为 \(N\) 的整数序列 \(A = (A_1, A_2, \ldots, A_N)\)\(B = (B_1, B_2, \ldots, B_N)\)

请输出满足 \(1 \leq l \leq r \leq N\) 的整数对 \((l, r)\) 的个数,使得下列条件成立:

  • \(\min\lbrace A_l, A_{l+1}, \ldots, A_r \rbrace + (B_l + B_{l+1} + \cdots + B_r) \leq S\)

其中 \(n\le2e5,S\le 3e14\)

思路

看到题目中的条件时首先观察到 \(min\) 操作是可以按照建立笛卡尔树的方法去进行操作的,我们在笛卡尔树上找出以 \(x\) 为最小值的区间之后,问题就转化成了求出使得 \(l\le x\le r\) 的区间,且这个区间的 \(sumb_r-sumb_{l-1}\le S-a_x\)

我们考虑如何求出这样的区间,不难发现在确定一个端点时,另一个端点的上界是可以 \(\log(n)\) 的快速求出的,但是暴力枚举的时间复杂度是 \(n^2\) 的,无法接受。考虑启发式合并的思路,每次枚举的时候都去遍历子树大小较小的一边,此时我们的时间复杂度就变成了 \(O(n\log^2n)\)

CF2111G

题意

对于一个长度为 \(m\) 的数组 \(a\),如果满足以下两个条件之一,则称其为“可分数组”:

  • 存在一个下标 \(i\)\(1 \le i < m\))和一个整数 \(x\),使得对于所有下标 \(j\)\(j \le i\)),都有 \(a_j \le x\),并且对于所有下标 \(k\)\(k > i\)),都有 \(a_k > x\)
  • 存在一个下标 \(i\)\(1 \le i < m\))和一个整数 \(x\),使得对于所有下标 \(j\)\(j \le i\)),都有 \(a_j > x\),并且对于所有下标 \(k\)\(k > i\)),都有 \(a_k \le x\)

现在给定一个 \(1\)\(n\) 的排列 \(p\)。你需要高效地回答如下询问:对于排列的区间 \([l, r]\),即 \(p_l, p_{l+1}, \dots, p_r\),该子数组是否为“可分数组”?

其中 \(n\le2e5,q\le 1e6\)强制在线

思路

若把 \(a_i\le x\) 的节点全部标记成 \(1\),那么我们此时的问题就自然转化为了,求这个区间能不能被划分成一个连续段 \(1\) 和一个连续段 \(0\) 的组合。

若直接枚举区间左端点,去找另一个右端点,这肯定是不好操作的,因为我们每次加入一个数能够使得区间成立的 \(x\) 都会改变。所以我们想到去枚举 \(x\) ,观察到在每次 \(x\) 增加的时候都会有 \(O(1)\) 个连续段被拆分,那么对于形成的新连续段 \(l_1\sim r_1(1),l_2\sim r_2(0),l_3\sim r_3(1)\) ,新增的合法区间是 \(l\in[l_1,r_1],r\in[l_2,r_2]\)\(l\in[l_2,r_2],r\in[l_3,r_3]\),那么此时的问题就变成了 矩形加,单点查询,可以使用主席树维护

CF2128E2

题意

对于一个长度为 \(m\) 的数组 \(b\),如果整数 \(v\) 满足以下条件,则称 \(v\)\(b\) 的中位数:

  • \(v\) 大于等于数组中至少 \(\lceil \frac{m}{2} \rceil\) 个元素,并且
  • \(v\) 小于等于数组中至少 \(\lceil \frac{m}{2} \rceil\) 个元素。

给定一个整数 \(k\) 和一个由 \(1\)\(n\) 之间的整数构成的数组 \(a_1, \ldots, a_n\)

如果存在至少一对下标 \((l, r)\) 满足:

  • \(1 \leq l \leq r \leq n\)
  • \(r - l + 1 \geq k\)
  • \(v\) 是子数组 \([a_l, \ldots, a_r]\) 的中位数,

则称 \(1\)\(n\) 之间的整数 \(v\) 是一个“子中位数”。

请找出所有的子中位数,并且对于每一个子中位数,给出任意一组对应的下标对 \((l, r)\)

其中 \(n\le 2e5\)

思路

我们先考虑子问题,如何求出区间的最大和最小中位数?这显然是好做的,考虑到中位数的 \(1 \text{&} -1\) 技巧,我们可以二分最大或最小中位数,把小于\大于 \(mid\) 的数标为 \(-1\) ,其余标记为 \(1\) ,那么对于一个合法中位数我们一定可以找到一个区间使得区间和 \(\ge 0\)

我们再次考虑一个事实,考虑一个区间加入数字对中位数的影响,每加入\删除一个数字的时候中位数最多偏移 \(O(1)\) 段,那么我们从最小中位数区间转移到到最大中位数区间的过程中一定会遍历到所有可能的中位数。

CF2096F

题意

共有 \(m\) 条编号为 \(1\)\(m\) 的陈述,每条陈述要么为真,要么为假。对于每条从 \(1\)\(m\)\(i\),陈述 \(i\) 属于以下两种类型之一:

  • \(0\:a_i\:b_i\)\(1 \leq a_i \leq b_i \leq n\))——在观众 \(a_i, a_i + 1, \ldots, b_i\) 中没有冒名顶替者;
  • \(1\:a_i\:b_i\)\(1 \leq a_i \leq b_i \leq n\))——在观众 \(a_i, a_i + 1, \ldots, b_i\) 中至少有一名冒名顶替者。

回答 \(q\) 个以下形式的问题:

  • \(l\:r\)\(1 \leq l \leq r \leq m\))——陈述 \(l, l + 1, \ldots, r\) 是否可能全部为真?

其中 \(n\le 2e5\)

思路

转化题意,对于 \(0\) 操作,区间赋 \(1\) ,对于 \(1\) 操作,查询区间是否存在 \(0\)

那么我们对于一个区间 \(l\sim r\) ,可以把所有的 \(0\) 操作提前,对于 \(1\) 操作查询,时间复杂度 \(O(qn\log n)\)

考虑优化,发现合法区间单调,可以拿双指针维护,但是这样无法实时对于 \(1\) 操作检查合法性。因为 \(1\) 操作是否合法是与 \(0\) 操作直接相关的,那我们考虑如何把 \(1\) 操作的检验放到 \(0\) 操作上去实现,即在加入 \(0\) 操作时判断是否会对于一个 \(1\) 操作使其由合法变为不合法。

可以考虑在区间赋值为 \(1\) 时,判断是否有被完全包含的 \(1\) 操作的询问区间,具体的,把 \(1\) 操作放到左端点的线段树上,维护每个左端点的最小右端点(在线段树的叶子结点上维护一个multiset),在加入 \(0\) 操作实时找到新增的 \(1\) 连续段,查询右端点最小值是否存在小于新增连续段的右端点,实时移动双指针。

拓展

对于类似的 \(1\) 号操作会使得一个区间变得符合/不符合题意,对于所有的 \(2\) 号操作该区间是否合法/不合法,即 \(2\) 号问题的答案与 \(1\) 号问题直接相关的,我们可以考虑把 \(2\) 号操作的判定放在 \(1\) 号操作上进行。

CF2129E

题意

给定一个无权无向图 \(G\),包含 \(n\) 个节点和 \(m\) 条边。图 \(G\) 中没有自环和重边。

我们用 \(V\) 表示 \(G\) 的节点集合。对于任意节点子集 \(V' \subseteq V\),其对应的导出子图记作 \(G[V']\)

你的任务是回答 \(q\) 个询问。每个询问给出三个整数 \(l\)\(r\)\(k\)。令 \(V' = \{l, l+1, \ldots, r\}\),你需要在 \(f(l, G[V'])\)\(f(l+1, G[V'])\)\(\ldots\)\(f(r, G[V'])\)\(r-l+1\) 个值中,找出第 \(k\) 小的值(即按升序排列后的第 \(k\) 个值,重复值按多次计)。

其中,\(f(u, G[V']) = \bigoplus_{(u,v)\in G[V']} v\)。也就是说,\(f(u, G[V'])\)\(G[V']\) 中与节点 \(u\) 相邻的所有节点编号的按位异或值。

其中 \(n\le 2e5\)

思路

首先发现这个题若使用扫描线等方法是无法进行的,因为对于一个左端点 \(l\) 的改变会影响到所有 \(l\sim r\) 的值,且求第 \(k\) 大的操作也不好维护。考虑莫队,对于普通莫队,在每次实时加入一个点时会遍历所有出边,现在这个问题变成了 \(O(n\sqrt n\times n\log n)\) 的。

我们发现只有在遍历到度数极大的点的时候才会导致时间复杂度暴增,考虑按照 度数 分块,此时时间复杂度就变为了 \(O(n\sqrt m\times \log n)\)

还是无法接受,我们发现此时的瓶颈在于插入一个节点时的 \(set\) 排序,而在查询时的时间复杂度只有 \(O(\log n)\) 的,复杂度不平衡,考虑如何改变排序方法。考虑值域分块,每次插入一个数字的复杂度就变成了 \(O(1)\),此时的查询操作变成了 \(O(\sqrt n)\)

CF1942F Farmer John's Favorite Function

题意

有一个长度为 \(n\) 的数组 \(a\)。还有一个函数 \(f\),其递推关系如下:

  • \(f(1) = \sqrt{a_1}\)
  • 对于所有 \(i > 1\)\(f(i) = \sqrt{f(i-1) + a_i}\)

注意 \(f(i)\) 不一定是整数。

他计划对数组进行 \(q\) 次更新。每次更新,他会给你两个整数 \(k\)\(x\),并希望你将 \(a_k\) 设为 \(x\)。每次更新后,他想知道 \(\lfloor f(n) \rfloor\) 的值,其中 \(\lfloor t \rfloor\) 表示将 \(t\) 向下取整到最近的整数。

其中 \(1 \leq n, q \leq 2 \cdot 10^5\)\(0 \leq a_i \leq 10^{18}\)

思路

观察发现函数的小数部分无用,因为若 \(f(i)\) 存在小数部分,但 \(a_{i+1}\) 是整数,所以不会影响答案,所以每次递推都可以直接向下取整。

又观察到一个 \(1e18\) 的数最多开 \(6\) 次就能变为 \(1\) ,且每次修改操作结束后查询时都需要再次遍历递推式,所以考虑扫描线+势能线段树。

线段树维护时间点,扫描线数组下标 \(i\),每次加入一个数字 \(a_i\) 时势能会增长 \(\log*6\) ,最多只有 \(n+q\) 次修改,全局开根,所以时间复杂度为 \(n\log n\)

拓展

势能线段树可以维护 区间赋值+区间 \(gcd\)(维护区间 \(lcm\))/区间开根(维护区间 \(\max\) 和区间 \(\min\))……,遇到类似操作大胆尝试写线段树

P9984 [USACO23DEC] A Graph Problem P

题意

给出一张连通的无向图,包含编号为 \(1\dots N\) 的节点和编号为 \(1\dots M\)\(2 \le N \le 2\cdot 10^5\)\(N - 1 \le M \le 4 \cdot 10^5\))的边,下边的操作将被实施:

  1. 假设集合 \(S=\{v\}\),变量 \(h=0\)
  2. \(|S|<N\),重复执行:
    1. 仅有一个顶点在集合 \(S\) 中的边中,找到编号最小的那条,编号记为 \(e\)
    2. \(e\) 不在 \(S\) 中的那个顶点加入集合 \(S\)
    3. \(h\) 修改为 \(10h+e\)
  3. 返回 \(h\)\(10^9+7\) 取模的值。

对每个 \(v\) = \(1\sim n\),输出这个过程的返回值。

思路

这个过程相当于 \(prim\) ,又因为所有边权不同(边权为编号),所以最后的最小生成树确定。

我们若在 \(prim\) 的算法过程中统计答案,不好处理起点不同的情况,所以我们可以直接考虑把 \(prim\) 转为 \(kruskal\) ,对于最小生成树中权值最大的边 \(u-v\) ,对于以 \(u\) 联通块(记为 \(C_u\))内的点为起点时,这条边肯定是在所有 \(C_u\) 内的点都被加入之后才被选中,然后以 \(v\) 为起点遍历 \(C_v\),所以我们 \(C_u\) 中的点都可以直接继承 \(ans_v\) 此时的答案,此时 \(x\in C_u,ans_x=10^{siz_{C_v}}\times ans_x+10^{siz_{C_v}-1}\times val+ans_v\) ,其中 \(val\) 为当前边的权值。

这个过程和kruskal 重构树的性质相同,我们可以在 kruskal 重构树上实现,并使用并查集或线段树维护。

P11191 「KDOI-10」超级演出

题意

巡准备了一场超级演出 Ciallo。舞台和候场室可以看作一个包含 \(n\) 个点 \(m\) 条边的有向图DAG。

舞台为 \(1\) 号节点,保证所有节点均有到达节点 \(1\) 的路径。其余的节点均为候场室,每个候场室恰有一个剧团进行等待。

巡可以对一个候场室 \(u\) 发布出场命令:

  • 如果这个候场室的剧团还没有出场,并且存在一条 \(u\to 1\) 的路径上没有其余候场的剧团。那么这个剧团就会沿着这条路径到达舞台进行演出,随后退场。注意:一个剧团退场后不会重新回到候场室。
  • 否则,这个命令被认为是无效的。

巡有一个命令序列 \(a_1,a_2,\dots,a_k\)\(q\) 次询问,每次给出一个区间 \([l,r]\)。巡想要知道如果依次对候场室 \(a_l,a_{l+1},\dots,a_r\) 发布出场命令后,候场室还会剩下多少剧团等待演出,询问独立。

其中 \(n\le 2e5\)

思路

对于每个命令 \(a_i\) 若其能够生效,那么最大的左端点 \(q_i\) 一定是由他们的出边能够生效的最大左端点决定的,而最小左端点 \(p_i\) 显然取决于上一次 \(a_j=a_i\)\(q_j\),即 \(p_i=q_j+1\) 。所以对于每个 \(l\in [p_i,q_i]\)\(r\in [i,n]\) ,都会使得 \(a_i\) 剧团进行演出,可以使用树状数组+扫描线维护。

但是我们若直接枚举 \(a_i\),再暴力枚举出边,此时的时间复杂度必定爆掉,因为一个出度很大的点可能会在 \(a\) 序列中出现多次,此时的时间复杂度就会退化到 \(n^2\) 。考虑优化,我们考虑到因为无法完全遍历一个出度很大的点,所以我们不妨让出度很小的点去主动更新出度很大的点。具体的,我们考虑把度数大于 $\sqrt m $ 的点称作 大点 ,反之为 小点 ,所以我们每次查询的时间复杂度就变为了 \(\sqrt m\)

P10846 [EGOI 2024] Team Coding / 团队编程

题意

公司是一个 \(n\) 点有根树,每名员工都有一个编程语言 \(l_i\),需要执行以下操作:

  1. 选择一个人来领导团队。这也将确定项目使用的编程语言。每个在该团队领导下的子树中的员工且首选编程语言与团队领导相同的员工将参与这个项目。

  2. 通过将首选编程语言与团队领导相同的员工调入她的团队来增加参与项目的员工数量。

    为了最大化参与项目的员工数量,她可以多次执行以下调换操作:

    1. 她选择两名深度相同的员工:
      • 一名当前在团队领导的子树中且首选编程语言与团队领导不同的员工。
      • 一名当前不在此子树中且首选编程语言与团队领导相同的员工。
    2. 交换这两名员工

找出能够参与新项目的最大员工数量,以及实现这一目标所需的最少调换次数。

其中 \(n\le 1e5,l_i\le 1e5\)

思路

容易发现若确定了根 \(r\),那么问题一答案即为 \(\sum_{i=deep_r}^n \min(cnt_{i,l_r},siz_{r,i})\),问题二即为 \(ans1-sum_r\),其中 \(cnt_{i,j}\) 表示深度为 \(i\) 的语言为 \(l_i\) 的人数,\(siz_{r,i}\) 表示以 \(r\) 为根的深度为 \(i\) 的节点数,\(sum_r\) 为以 \(r\) 为根的同色数。

考虑到 \(siz\)\(sum\) 都是可以通过启发式合并直接搜出来的,我们不好处理的只有 \(cnt_{i,j}\) ,所以我们的问题就转化成了 \(cnt\) 的求法。

因为颜色数过多,直接存入数组显然不好做。那我们不妨直接尝试暴力枚举,但这样的时间复杂度是 \(s^2\) 的,其中 \(s\) 是当前颜色的数量。但是我们可以发现若选择 \(r\) 为根,那么从 \(1\)\(r\) 的路径上不可能再次出现 \(l_r\),正确性显然。并且枚举的深度最大值就是当前 \(r\) 到底部的深度,所以时间复杂度变为 \(\frac{n}{s}\min(n,s^2)=n\sqrt n\)

posted @ 2025-09-12 11:26  xtp  阅读(14)  评论(0)    收藏  举报