noip 前杂题选做

111从 noip 前做题记录里面找的。

CF814E

hba 模拟赛本题数据范围为 \(n\leq 1000\)\(n\leq 50\) 可以让 \(\mathcal O(n^4)\) 甚至 \(\mathcal O(n^5)\) 的无脑 dp 做法肆意通过。

先对图进行 bfs 分层,处理出 \(l_i\) 表示到 \(1\) 的边数。显然任意一条边的端点 \(|l_u-l_v|\leq 1\)。由于每个点到 \(1\) 的最短路唯一,所以提取出 \(|l_u-l_v|=1\) 的边 \((u,v)\),显然构成的是一棵以 \(1\) 为根的连通树。并且由于 \(l\) 随标号增加而不减,因此对于相同的 \(l\),一定是原标号的一段区间。考虑对于同层的连边一定是若干环,单点,与链。除了 \(1\) 号节点,我们可以将 \(d\) 减去一表示对父亲的连边,那么剩余的 \(d\) 就分为向下一层以及向同层连边。

考虑以分层来划分阶段,注意到当前是第几层完全不重要,我们只关心当前最后一层的信息,即令 \(f_{i,j}\) 表示考虑到第 \(i\) 个节点,最后一层是最后 \(j\) 个节点的方案数,因为要考虑父节点连边,我们只关心是所有还差一条连边的,还需要从下面往上连的是 \(j\) 个节点。那么就可以确定本层会划分恰好 \(j\) 个点,选取父节点的方案数就是 \(j!\)。重点在于本层的划分情况,标号上就是 \([i+1,i+j]\) 的所有节点,显然我们只关心其中 \(1/2\) 度点的个数(不考虑与父亲连边),分别记作 \(x\)\(1\) 度点以及 \(y\) 个二度点,目标是转移到 \(f_{i+j,k}\) 上。此时要求 \(2\mid (x+2y-k)\),该层内部会连 \(t=(x+2y-k)/2\) 条边。问题在于如何分配。

考虑到 \(1/2\) 度点的特殊性,不难想到将 \(2\) 度点拆成两个点。此时就是要求选出 \(2t\) 个点相邻进行连边,方案数即为 \(f(x,y,t)=(x+2y)!/((x+2y-2t)!2^tt!)\)。问题在于不允许自环与重边。考虑容斥,令 \(y\) 个二度点中有 \(a\) 对重边,即有 \(a\)\((u_1,v_1),(u_2,v_2)\) 以及 \(b\) 个自环 \((u_1,u_2)\),此时容斥系数为 \((-1)^{a+b}\),选法是 \(\frac{y!}{(y-2a-b)!a!b!}f(x,y-a-b,t-2a-b)\)。考虑将 \(2a+b=k\) 的所有贡献放在一起进行计算,交换求和顺序。要求 \(k\leq \min(y,t)\),此时有内部的容斥系数可以提出来计算。所以可以使用 \(\mathcal O(n^2)\) 的复杂度计算 \(coef_k=\sum_{2a+b=k}\frac{(-1)^{a+b}}{a!b!}\),那么答案就是 \(\sum_{0\leq k\leq \min(y,t)}coef_k\frac{y!(x+2y-2k)!}{(y-k)!2^y}\times\frac{1}{(t-k)!2^{t-k}}\),使用分步转移即可做到 \(\mathcal O(n^3)\) 的时间复杂度。

CF2039F2

先考虑将区间 \(\max\) 放到笛卡尔树上刻画,对于 \(f(k)\) 可以弱化为 \(\gcd_{i\ge k}\lbrace f(i)\rbrace\)。那么就可以转化为笛卡尔树上所有子树大小 \(\ge k\) 的节点的 \(\gcd\),由于任意 \(k\)\(f(k)\) 均不相同,所以一个必要条件是 \(\text{size}\) 必然连续,也就是对于子树大小为 \(1\sim n\) 的点是都存在的。换而言之,其笛卡尔树的形态是一条链,即原序列单谷,并且按照链的顺序从上往下所有点的 \(\gcd\) 是严格递减的。那么就有一个 dp 就是 \(f_{i,j}\) 表示当前链的结尾为 \(i\),当前位置的 \(\gcd=j\) 的方案数。转移需要枚举下一个位置的值 \(t\),如果 \(\gcd(j,t)<j\) 就转移到 \(f_{t,\gcd(j,t)}\),系数为 \(\times2\) 表示笛卡尔树选取左儿子还是右儿子。直接做是 \(\mathcal O(n^2\log n)\),不可接受。考虑优化就是调和级数以及容斥的思想,先求出 \(\gcd\mid d\) 的信息然后再容斥即可。时间复杂度 \(\mathcal O(\sum_{i=1}^nd(i)^2)\)。对于加强版,这样做是从后往前倒序遍历 \(n\)\(1\) 的,考虑拓展的话就必须是正向扫描线值,并且从小到大建立序列。那么就是类似于反转 dp,重新定义 \(f_{i,j}\) 表示当前结尾为 \(i\),在最终序列中从 \(i\) 开始的 \(\gcd=j\)\(2^{len-1}\) 的和,这个表示每一步选取左右的方案数。转移形如枚举上一个为 \(f_{x,y}\),当前 \(\gcd\)\(q\),那么就有转移 \(f_{x,y}\to f_{i,q}\),要求 \(y=\gcd(x,q)\)。对于每个 \(i\) 做莫比乌斯反演就可以对于每个 \(i\) 单个用 \(\mathcal O(\omega(i)d(i))\) 的时间解决,最终就是对这个求和的时间复杂度。

P8340

判定一个集合是否合法,可以采用归纳的思路。一个显然的结论是,对于任意最小的 \(k\) 个数构成的子集,其能表示的数一定是值域前缀 \([1,R_k]\),若不然,则中间被跳过的数是无法被弥补的。加入值 \(v_{k+1}\) 后就可以表示 \([1,R_k]\cup [v_{k+1},v_{k+1}+R_k]\),因此就要求 \(v_{k+1}\leq R_k\)。从值域的角度进而可以推知,对于任意 \(v\),满足 \(\sum [S_i\leq v]S_i\ge v\) 是子集合法的充要条件。

正常 dp 是无法做到比 \(\mathcal O(n^2)\) 以下的复杂度的。正难则反,因此考虑进行容斥。我们发现某个子集不合法一定是因为存在某个 \(v\) 使得 \(\leq v\) 的所有数的和 \(<v\)。对于每个不合法集合找到这个最小的 \(v\) 记为它的代表元。那么只需要求出对于每个 \(v\) 有多少个集合的代表元为 \(v\),方案数记作 \(f_v\)。那么所有不合法集合的总数就是 \(\sum f_{v-1}2^{n-v}\)。那么问题就是,对于 \(x\) 有多少个子集和为 \(x\) 并且可以表示出 \([1,x]\) 中的所有数。

考虑进一步容斥,用容斥来求出 \(f\) 的方案数。首先考虑求出 \(g_n\) 表示 \([1,n]\) 中若干个数和恰好为 \(n\) 的方案数。可以数形结合,对于 \(S\) 建立直方图,第 \(i\) 列有 \(S_i\) 个格子,要求每列格子数单调减,并且每行格子数与下一行的差值不超过 \(1\)。也就是考虑第 \(i\) 行有 \(a_i\) 个格子,要求对于 \(i>1\)\(a_i\in\lbrace a_{i-1}-1,a_{i-1}\rbrace\),要求最终和为 \(n\)。考虑对于行做完全背包,并钦定本行至少有一个,显然每行格子数 \(\leq 2\sqrt n\),直接背包的复杂度就是正确的。此时考虑求 \(f\),需要减去代表元在 \(n\) 之前出现的方案。\(f_n=\sum f_{i}w(i,n)\)。分析 \(w(i,n)\) 的意义就是在 \([i+1,n]\) 中选取和为 \(n-i\) 的方案数。注意所有元素应该 \(\ge i+2\),因此内层做背包时应该更改初值。使用 cdq 分治/倍增来求,时间复杂度 \(\mathcal O(n\sqrt n)\)

P8341

首先可以进行一遍 dfs 预处理出所有被别的路径完全包含的路径,这些路径显然是没有用的,可以树状数组维护 dfs 序区间维护,现在认为路径是互不包含的。可以对问题进行转化,对于两条直链如果可以在同一次行走中被消去就进行匹配,对于同一个类而言要求其可以一次消去。考虑从下往上进行贪心,那么一个子树 \(u\) 的状态就是若干条已经完成匹配(在 \(u\) 或以下,是当前链的上端点)以及若干条正在向上仍未完结的链(即当前链的上端点在 \(u\) 的祖先之中)。对于已经完成匹配的,可以直接记录 \(f_u\) 表示这个的条数,以及 \(c_u\) 表示子树中彻底完成匹配无法再接的链条数,我们显然不会关心它具体在哪里结束。对于未完结的链,可以使用数据结构维护其深度构成的可重集 \(S_u\),线段树或者堆或者平衡树皆可。而且对于未完结的链,一个明显的策略就是保留尽量深的路径。

先考虑合并儿子 \(v\)\(u\) 上。显然 \(S_v\) 中上端点不为 \(u\) 的需要直接继承,可以使用启发式合并,除此之外,我们还关心 \(g_v\) 表示 \(v\) 有多少条路径的上端点为 \(u\),我们希望与别的子树进行匹配。对于 \(f\) 可以直接累加。如果这些以 \(u\) 为上端点的可以相互匹配就先判掉,否则存在某个子树的待匹配路径还大于别的路径待匹配的和,可以拆到别的子树内已经匹配的路径来与这个重子树进行匹配。加入新的以 \(u\) 为下端点的路径时,如果 \(S_u\) 非空就将 \((u,v)\) 并到 \(S_u\) 深度最小的那个路径上;否则考虑子树内是否有已匹配路径,如果有就拆开成两条单链,其中一条来与 \((u,v)\) 匹配。需要 std::multiset 维护 \(S\),因此时间复杂度 \(\mathcal O(n\log^2n)\)

P10430

考虑先执行操作 \(1\) 再执行操作 \(2\)。我们需要用最少的操作 \(1\) 即若干次单点减 \(D\) 使得整个序列变得单调不降,可以考虑离线扫描线序列。显然 \([l,r]\) 的操作结果基于 \([l,r-1]\) 的操作结果。但是显然扫描线 \(r\) 时我们不能对于每个 \(l\) 都暴力处理。考虑找一下性质,对于一个 \(a_i>a_{i+1}\) 而言,\(a_i\) 必然操作过。且后续如果要操作 \(a_{i+1}\) 就必须要操作 \(a_l\)。并且为了最小化操作次数,减到合法时我们不会无故操作 \(a_i\) 但不操作 \(a_{i+1}\)。此时满足 \(a_{i+1}-a_i<D\)。此时两者的操作是同步的,可以合并成一个连续段,并查集维护即可。然后加入一个 \(a_r\) 时可以不断操作之前的连续段,会有一些合并操作,可能也会插入 \(a_r\)。连续段的均摊操作次数是正确的,直接线段树维护即可。时间复杂度 \(\mathcal O(n\log n+n\alpha (n))\)

QOJ7766

考虑特殊性质 \(q_i=i\) 的做法。显然我们需要将 \(1\) 找到放到第一位,其次是 \(2\),再其次是 \(3\),以此类推。考虑找到最小的 \(x\) 使得 \(\max[1:x]=x\)。令 \(u=x+1\),再找到 \(v\) 使得 \(\max[u:v]=v\),再令 \(u=v+1\),循环执行这个过程,这样序列就被划分为了 \(d\) 段,即极小值域连续段 \([l,r]\) 的数集为 \([l,r]\) 的划分数量。那么 \(f(p)=q\) 的充分必要条件就是 \(k\leq d\)。记录这个 \(d=h(p)\)。那么可以直接 dp,设 \(f_{i,j}\) 表示 \(i\) 的排列中有多少种 \(h(p)=j\)。对于 \(j>1\) 就是存在某个划分点,也即 \(f_{i,j}=\sum f_{k,1}f_{i-k,j-1}\)。对于 \(f_{i,1}\) 考虑归纳,可以在 \(i-1\) 的排列中插入,考虑 \(i-1\) 排列中首个划分段的位置 \(j\),必须在其中插入。此时 \(f_{i,1}=\sum f_{j,1}j(i-1-j)!\),或者容斥也是可以的。时间复杂度 \(\mathcal O(n^3)\)

考虑发掘函数 \(f\) 的性质,要想最小化 \(q_1\) 就必须在 \([1,n-k+1]\) 中取最小值。然后删去 \(q_1\) 考虑 \(q_2\) 的最小值,以此类推。逐位考虑,在 \(i\) 结束时进行分段要求 \(q_1\sim q_i\) 恰好落在 \(p\) 的前 \(i\) 位,那么在前面能分段就分段。你可以先全选 \([1,n-k+1]\),然后从后往前逐个删除直到前面的排序后字典序发生变化,这样做可以满足段数最少。然后在 \(i\) 断开就有一个 \(\max(a_{1\sim i})<\min(a_{i+1\sim n-k+1})\) 的子结论,显然此时你会寻找最小满足这个的 \(i\),对于后面是同理的。

此时将 \(q\) 一般化成无序序列。由于最终每一个子段是有序的,注意到所有 \(q_i>q_{i+1}\)\((i,i+1)\) 都必然是断点。记其中的第一个是 \(x_c\),下一个断点是 \(t\),结论是 \(t\) 再之后的断点必然是连续的,即每个段长为 \(1\)。也即合法序列形如:\([1,t)\) 递增,被分成若干段;\([t,t')\) 递增,长度不为 \(1\) 就有 \(q_{t-1}<q_{t+1}\)。枚举 \(t'\)\(t\) 是唯一的,此时贡献为 \((t'-t-1)!f_{t,k-n+t'}\)。时间复杂度 \(\mathcal O(n^3)\)

QOJ9641

  • 构造题从特殊情况入手,增量构造思想,数形结合思想,极值思想。

考虑将 \([x_i,y_i]\) 视为区间 \(I_i\),那么一个区间中会进入集合的就是与 \(I_i\) 相交但不包含的所有 \(I_j\)\(j\)。因此这个数集的 \(\text{mex}=v_i\) 就告诉我们 \(I_i\)\(I_{1\sim v_i-1}\) 均是相交但不包含关系的,但是与 \(I_{v_i}\) 是包含或者相离关系,本题中不会出现相离。在判断有无解时,显然 \(v_i>i\) 是无解的,因为显然 \(I\)\(I\) 自身是不满足相交且不包含的。因此考虑建边 \(i\to v_i\) 来描述。对于包含关系就有 \(x_i<x_j\)\(y_j<y_i\)。直接猜测 \(v_i\leq i\) 都成立时就是有解的。直接放到二维平面上,视为点 \((x_i,y_i)\),那么就要求 \(i\)\(1\sim v_i-1\) 的点都是顺序对,但是与 \(v_i\) 是逆序对。直接进行增量构造,我们可以考虑让当前加入的点都是 \(x/y\) 维的极值。另一个就是考虑进行黑白染色,对于黑点加入时是 \(x\)\(\max\),对于白点 \(y\) 加入时是 \(y\)\(\max\)。然后对于另一维设置成父亲的前驱即可。使用链表分开维护 \(x,y\) 的相对顺序,时间复杂度 \(\mathcal O(n)\)

QOJ7771

首先考虑 \(\mathcal O(nq)\) 做法。一个显然的结论是第 \(i\) 次操作前第 \(i\) 个位置上的值是原序列 \(a_{1\sim i}\) 的最小值,考虑使用 01 的 trick,将 \(\geq x\) 视为 \(0\)\(<x\) 的视为 \(1\)。那么一次有效操作就是将 \(a_i=1\) 置换后面第一个 \(a_t=0\)。那么所有在某个 \(x\) 下会产生交换的轮次就是需要计入贡献的。我们显然只需要考虑 \(x\in(1,n]\)。一个结论是第 \(i\) 次操作只需要关心 \(x=n+1-i\) 的情况。那么考虑对于一个 \(x\) 的最终形态就是前 \(n-x+1\) 个都是 \(0\),后面都是 \(1\)。因此提前到达这个状态就可以少一次操作。考虑将 \(0/1\) 复原,我们需要计数有多少个前缀 \([1,x]\)\(\min\) 为从大到小排序后的第 \(x\) 个位置的值。

考虑如何处理多组询问,倒序扫描线 \(l\) 然后维护 \(r\)。考虑区间 \(\min\) 使用单调栈来维护出当前所有前缀最小值 \(v\) 的位置。记录 \(len\) 是前缀 \(\min\) 控制的长度,那么对于单调栈上一个最小值的段而言,可行的右端点位置就是最小值后面 \(1\sim len\) 个大于 \(v\) 的位置。使用 st 表上倍增来跳,树状数组维护。时间复杂度 \(\mathcal O(n\log n)\)

APIO2021 T3

考虑给定 \(k\) 之后的 dp 就是 \(f_{u,0/1}\) 表示 \(u\) 子树内满足要求的最小代价,使得 \((u,fa)\) 的边断开或者不断开。转移形如 \(f_{u,1}\) 求和后对前 \(k/k-1\) 大的 \((f_{u,1}-f_{u,0})\) 进行求和,对于 \(f_{u,1}\) 而言最终还需要加上 \(w_{E(u,fa)}\),时间复杂度是 \(\mathcal O(nk\log n)\)。对于所有的 \(k\) 求一遍 dp 值显然是劣的。观察一下解的形态,对于当前度数 \(\leq k\) 的点我们可以自动做出全部保留的决策,也就是对子树的 \(f_{u,0}\) 进行求和直接进行转移。而度数 \(>k\) 的点我们对于每个 \(k\) 暴力找一遍的复杂度是均摊正确的,由调和级数总点数是 \(\mathcal O(n\ln n)\) 的,问题是如何进行 dp 的维护。

注意到一个 \(\leq k\) 度数的点 \(p\) 在后续决策中并没有用,我们考虑删去这个点而将与其相邻的 \(>k\) 度的点 \(u\) 的边的边权丢进 \(u\) 的决策里,因为 \(p\) 的作用仅仅只是对于子树的 \(f\) 进行求和,向上汇总,此时 \(f_{u,1}-f_{u,0}\) 是确定的 \(w_{E(p,u)}\),我们只关心差值,因此 \(p\) 相当于把这些部分割成了独立的连通块。对于每个连通块可以任选一个根开始 dfs,注意到一个边的决策在后续是永久存在的,我们希望在 \(k\to k+1\) 的过程中继承一些信息,考虑将大度点上传的 \(\Delta f\) 视为临时决策,在求出 \(f_{u}\) 之后撤销这些决策,使用带删堆或者 std::multiset 维护即可,在加入删除时维护数据结构中所有数的和即可,时间复杂度 \(\mathcal O(n\log^2n)\)

QOJ7793

考虑用一个二叉树来描述合并的过程,每个叶子代表一本书,有输入的属性 \(a\) 以及初始为 \(0\) 的属性 \(b\)。对于非叶子节点 \(u\) 满足 \(a_u=a_{ls}+a_{rs},b_u=2\max(b_{ls},b_{rs})+1\),贡献为 \(b_{ls}+a_u\),其中 \(u\) 的两个儿子 \(ls,rs\) 表示将书 \(ls\) 叠合到书 \(rs\) 上。对于 \(a\) 我们是好刻画的,在树的形态确定时只需要求出每个叶子节点的深度 \(d_i\)(令根节点深度为 \(0\)),那么对于 \(a\) 的贡献就是 \(\sum d_ia_i\)。设 \(a\) 是从小到大排序的,如果想要最小化重量部分的和一定就是让 \(d\) 从大到小进行排序。 注意到 \(b\) 是与 \(a\) 是什么无关的,是由树的形态决定的,并且一个事实是 \(b=2^k-1\),其中 \(k\) 表示到子树中最远的叶子节点的路径所经过的边数。\(-1\) 是常量,我们可以不管,在最后给答案减去 \(n-1\) 即可。那么对于 \(b\) 就是所有左子树的 \(\text{maxlen}\)\(2\) 次幂和。想要最小化的话,就是对树进行长链剖分,需要计入所有非最长链上的点的 \(b\),因为你显然会让长链变为最右链,因为每个点的儿子个数要么是 \(0\) 要么是 \(2\),此时链顶向上的边必然为会造成贡献的虚边,所以再转化一下可以将这些 \(2^k\) 求和变成所有除去最长链的 \(2^{len}\) 之和。

现在考虑确定序列 \(d\) 的做法,现在要求安排树的形态使得 \(2^{len}\) 之和最小,可以从底往上构建这棵二叉树。考虑 \(l_u\) 表示 \(u\) 走到其子树内某个叶子节点最远要走多少条边,显然一个点的 \(l_u=\max(l_{ls},l_{rs})+1\)。从长链剖分的角度来看,会造成 \(2^{\min(l_{ls},l_{rs})}\) 的贡献表示某个长链终结于此。那感性理解一下 \(\min(l_{ls},l_{rs})\) 一定是大的被尽早计算贡献,考虑将这个过程视为子树合并,每次取出当前 \(d\) 最大的,合并成 \(d-1\),那么 \(l\) 从大到小排序后,我们一定会将排名 \(1,2\) 的进行合并,排名 \(3,4\) 的进行合并,以此类推,否则其向上的贡献会更大。考虑计算 \(b\) 造成的代价,可以隐约感受到这是一个分层的过程,倒着扫这个层,维护其有 \(x\) 个从下面合并上来的,\(y\) 个叶子节点。对于链剖分可以在链底即叶子节点考虑所有贡献,那么考虑新的叶子节点所在长链对答案的贡献,此时所有点依次排开深度递减,在二进制意义下向左定义为长链,可以 \(\text{pushback}\) 一个 \(0\),否则是 \(1\),那么就不难发现是 \(\sum_{i=x}^{x+y-1}\text{lowbit}(i)\),其中 \(\text{lowbit}(0)=0\) 表示包含根的最长链没有贡献。

这是一个很利于 dp 的形式。直接分层从下往上进行 dp。观察 \(b\) 的计算贡献式,我们只关心 \(x,y\),也就是还剩下多少节点没有分配,以及本层分配了多少个叶子。我们可以将 \(a\) 的贡献给拆到分每一层时进行计算。先将 \(a\) 进行排序,那么分给某一层的点就是当前剩余节点的一段前缀。加入一个叶子时就考虑新开一层还是并到当前的层,\(f_{i,j}\) 表示已经分了 \(i\) 个,当前层已有 \(j\) 个的最优代价即可。时间复杂度 \(\mathcal O(n^2)\)

QOJ9533

考虑确定下 \(\min,\max\) 之后连通块必然是唯一的,首先要求将两者连通,然后拓展相邻的 \(v\in[\min,\max]\) 的点直到不存在为止。也就是说只保留 \([\min,\max]\) 之间的点考虑其连通块。那么仅考虑一端限制时,就有 \(T(u)\) 表示以 \(u\) 为最小值的极大连通块,\(S(u)\) 表示以 \(u\) 为最大值的极大联通块,容易并查集维护。然后对于一个确定 \(\min,\max\) 的连通块就可以描述为 \(S(\max)\cap T(\min)\)。要求 \(\min\in S(\max),\max\in T(\min)\) 表示两者连通。具体的,\(S,T\) 对应的重构树都是类似于点分树的结构。对于这个重构树而言,其 \(S,T\) 就是以 \(u\) 为根的子树。

考虑固定 \(\max\)\(u\),那么就是在 \(S\) 树上进行 dfs。\(S_u\) 就是确定的一个子树,我们要求 \(v\in S_u\),统计 \(\sum vC(u,v)\),表示 \(T_v\)\(S_u\) 中的点的个数。有一个显然的类似于 qoj9492 的根号做法,不赘述。考虑对 \(T\) 树进行轻重链剖分,并且考虑拆贡献,对于每个在 \(S_u\) 中的点将所有祖先的贡献 \(+1\)。记贡献系数为 \(b\),原标号为 \(a\) 表示恒有贡献,\(c_i\in\lbrace0,1\rbrace\) 表示是否会造成贡献。那么就要求支持 \(b\) 的区间加正负 \(1\)\(c\) 的单点翻转,以及区间 \(\sum a_ib_ic_i\) 的和。容易用线段树维护。对于 \(c\) 而言可以在 \(S\) 树上使用 DSU on tree。总共会执行 \(\mathcal O(n\log^2n)\) 次操作,时间复杂度 \(\mathcal O(n\log^3n)\)

QOJ9561

  • 找性质的一步中可以从最基础的情况开始思考,然后逐步拓展。

这题不是我们 NOI2023 D1T2 吗。\(V\) 显然是没用的,因为取 \(S=\emptyset\) 以及 \(S=U\) 时就可以导出 \(\min(U)=0\) 以及 \(\text{mex}(U)=V+1\)。显然 \(0\)\(U\) 中有且只有一个,考虑 \(0/1\) 之间的联系。直接让唯一的那个 \(0\) 是整个树根。考虑这些 \(1\) 构成的虚树,虚点认为不影响权值。不难发现任意同时包含 \(0,1\) 的虚树都必须包含全部的 \(1\)。那么考虑拓展这个结论到 \(i\),可以插入虚树 \(T_{i-1}\),要么插入到边上,要么加入一个叶子。就是要求任意包含 \(0\sim i\) 的虚树必须包含全部的 \(i\)。直接考虑取极大的,判定能否剪掉 \(i\),也就是说对于任意权值 \(\leq i\) 构成的虚树而言,如果 \(i\) 出现了 \(>1\) 次,任意权值为 \(i\) 的点不能是 \(1\) 度点。

考虑直接设计 dp:\(f_{i,j,k}\) 表示 \(0\sim i\) 的权值满足虚树的节点个数为 \(j\),有 \(k\) 个点是无权值的。那么考虑分裂了 \(p\) 个点作为 \(i+1\),赋值了 \(q\) 个点作为 \(i+1\),转移为 \(f_{i,j,k}\binom{k}{q}\binom{p+j-2}{j-2}\to f_{i+1,j+p,k-q}\),后者是因为可以在原有虚边加入多个 \(i+1\)。 进行分步转移可以少枚举一个 \(n\)。对于挂在叶子上的情况,有两种情况:挂在某一个已有的节点上;或在某一条虚树边的上加入一个虚点然后挂在这个虚点上。分别为 \(f_{i,j,k}j\to f_{i+1,j+1,k}\) 以及 \((j-1)f_{i,j,k}\to f_{i+1,j+2,k+1}\)。时间复杂度 \(\mathcal O(Vn^3)\)

LOJ3276

  • dp 但是延迟处理妙完了。

考虑由 \(h\to p\) 的变化过程。显然第二个值为 \(n\) 的点就是会保留下来的,此时会将第一个值为 \(n\) 的点变为 \(n-1\),第一个值为 \(1\) 的点被删去。此时 \(1\sim n-1\) 都恰好有两个数。令 \(R_{i,0/1}\) 表示数值 \(i\)\(1/2\) 次出现的位置。考虑值为 \(n-1\) 的点,位置就是 \(\max(R_{n,0},R_{n-1,1})\)\(\min\) 被递归到 \(n-2\) 的问题,以此类推。但是从数值的角度来讲是非常复杂的,考虑倒序加入整个序列,对于高效地维护下降的过程而言,我们发现如果后续已经存在了这个数值的确定位置,那么它就一定会自减向前传递。因此,考虑维护一个序列 \(B\),初始全部为 \(0\)。倒序枚举 \(i\),再倒序从 \(h_i\)\(1\) 来枚举 \(j\),如果 \(B_j=0\) 就将 \(B_j=i\) 并终止循环,不存在则会消失。那么 \(B_i\) 即为值 \(i\) 的位置,也即有值的 \(p\) 的构成集合就是 \(B\)

注意到我们需要判定 \(i\) 能否成功插入 \(B\),如果成功插入就要求 \(B\) 中有值的 \(i\) 构成的数值集合的 \(\text{mex}\) 必须 \(\leq h_i\)。其中 \(\text{mex}\) 定义是正整数。可以区分一下相同的 \(h\),最终除以 \(2^n\) 即可。那么 dp 就是考虑了 \(i\sim 2n\),当前数集的 \(\text{mex}=j+1\) 的方案数 \(f_{i,j}\)。初始有 \(f_{2n+1,0}=1\),最终答案为 \(f_{1,n}/2^n\)\(p\) 相当于告诉了你所有插入失败的位置。考虑将 \(\text{mex}\) 改变的位置视为 dp 的关键节点。

首先考虑 \(i\not\in P\) 的情况,此时插入失败,要求 \(h_i\leq j\),转移的 \(\text{mex}\) 是不变的,此时转移系数为 \(j\) 减去 \([i+1:2n]\) 中插入失败的数的个数,因为一个必要条件是这些插入失败的数都 \(\leq j\)\(\text{mex}\) 具有单调性。其次考虑 \(i\in P\) 的情况,如果 \(\text{mex}\) 是不变的,此时我们一个性质是它不会作为最后一个 \(B\) 被插入,因为最后必然会更改到 \(n+1\),如果在此时贸然进行转移,会导致信息失效,因此先将系数搁置,对于 \(h_i>j\) 的直接转移;而 \(\text{mex}\) 改变的位置是关键的。我们可能决策的是多个位置的值,考虑 \(\Delta\text{mex}=k\)。显然会有 \(h_i\) 掉到 \(j+1\) 的位置上,然后 \(j+2\sim j+k\) 的位置都是已经填写的。考虑选出这 \(k+1\) 个位置并填写 \(h\)。当前我们认为已有值的就是 \([1,j]\),所以 \(h\) 也只能在 \([j+1,j+k]\) 中取,要求 \(h_i\leq j+t\)\(i\) 不超过 \(t\) 个,否则就有没有位置的,这个与 \(j\) 无关,可以直接预处理系数 \(coef_k\),还要考虑 \(h_i\in[j,j+k]\),此时有 \(k+1\) 种取法,然后还要再选出这些位置,这是组合数。时间复杂度 \(\mathcal O(n^3)\)

CF2029H

(无向图)状压计数题中,容斥是常见的思想,联合省选 2024 就出过。

题目中给的特殊性质就是对于 \(S\),拓展到 \(S\subseteq T,S\ne T\) 的期望步数一定存在,这提示我们将贡献拆到集合上。考虑集合拓展的操作序列:时刻 \(1\sim t_1\) 的极大黑点是 \(S_1=\lbrace1\rbrace\),时刻 \(t_1+1\sim t_2\) 的极大黑点集合是 \(S_1\subseteq S_2,S_1\ne S_2\),以此类推,直到时刻 \(t_k+1\sim t_{k+1}\) 的极大黑点集合是 \(S_k\ne U\),在时刻 \(t_{k+1}\) 变为全集 \(S_{k+1}=U\)。其实有效的操作序列要满足 \(S\) 是互不相同的,定义一个操作序列的权值是期望步数,概率就是每步概率的乘积,只需要对此进行求和。由期望的线性性,我们可以将贡献拆到每一个集合 \(S\) 上。我们发现对于同一个时刻变化的区间内概率都是相同的,因此可以维护:在操作时有且仅有 \(S\) 集合被染成黑点的概率 \(f_S\),考虑当前为集合 \(S\),期望还需要多少步才可以拓展 \(S\) 到更多的节点上。答案即为 \(\sum f_Sg_S\)。显然 \(g_S\) 是容易 \(\mathcal O(2^nn^2)\) 求出的,由题目的特殊性质这个期望步数是一定存在的,答案也就是 \(\frac{1}{1-p}\)。对于 \(f\) 有显然的 \(\mathcal O(3^n)\) 做法,不可接受,并且系数都与 \(S\setminus T,T\) 都相关,信息量本身就难以接受。

瓶颈在于“恰好”拓展到 \(T\) 是很烦的。

山重水复疑无路。无法优化的话,考虑进行容斥。具体来说,维护一个 \(h_S\) 表示至多只有 \(S\) 中的点被染黑,也就是说 \(h_S=\sum_{T\subseteq S} f_{T}\)。考虑 \(f_S\to h_{S\cup T},|S\cap T|=0\) 的转移。转移只需要保证不会拓展到 \(S\cup T\) 以外的点就好了,并且需要减去 \(S\) 向外不拓展的概率。而两个集合之间的连边可以写成全集除以两个集合内部连边的乘积。转移系数就是两部分的概率乘积。维护 \(P(S)\) 表示点集 \(S\) 之中的边断开概率之乘积。系数容易拆到之和 \(U,S,T,S\cup T\) 有关。转移的形式就是 \(\text{val}(S)\text{val}(T)\text{val}(S\cup T)\to h_{S\cup T}\)。按照 \(|S|\) 从小到大做半在线子集卷积即可。具体就是用 FMT 来做变换,做点积后逆变换回去。时间复杂度 \(\mathcal O(2^nn^2)\)

CF2035G2

这个二分的过程是假的,因为原数列是不存在单调性的,但是分治结构以及判定的点是完全确定的。因此你可以建立出来线段树表示分治结构。那么下落到 \(x\) 的路径是确定的,我们也可以确定路径上经过的点与 \(k\) 的大小关系,向左走满足 \(a_{mid}\ge k\),反之有 \(a_{mid}<k\)。先考虑第一问做法。对于一个保留的限制集合,我们考虑是否存在一个 \(a\) 可以满足所有限制,那么就是每个 \(a_{1\sim n-1}\) 就都有一个取值区间,如果某个区间为空就挂了。注意到一个点管辖的就是一段区间,就要求 \([l,mid]\) 中的限制最大值,小于,\([mid+1,r]\) 中的限制最小值。然而事实上你可以直接将限制拓展到关于 \(mid\) 的前缀和后缀,只需要将父亲节点的限制拼起来就可以得到。因此判定有解的充分必要条件是保留的 \(k\) 如果按照 \(x\) 排序是单调递增的。那么第一问就是简单的,按照 \(x\) 排序后求最长上升子序列即可。

计数的一个思路就是对于 \(x\) 维护一下以它为结尾的最长长度,同时记录方案数。考虑如何从 \(f_i\) 转移到 \(f_{j}\),要求 \(x_i<x_j\)。那么考虑方案数就是维护所有被 \([1,i]\) 完全包含的节点的方案数,我们定义一个节点为一个区间,节点的方案数是其划分点的方案数。转移的系数就是 \((x_i,x_j)\) 的填数方案数。考虑暴力跳 \(i,j\) 的父亲表示需要特殊考虑的点,就是说取值必须在 \([lmx,rmn)\) 之中的点,剩余点是平凡的。因为树高是 \(\log n\) 的所以可以接受,转移就可以 \(\mathcal O(m^2\log n)\)。优化也显然,考虑枚举两者的 \(\text{lca}\)。由于是 \(\text{lca}\),那么对于 \(j<i\) 的话,\(i\) 一定是以右子树的身份跳上去,\(j\) 一定是以左子树的身份跳上去。我们可以预处理 \(\text{lca}\) 关于左子树以及具体的 \(j\) 的贡献 \(L_{u,i}\),以及右子树以及 \(i\) 的贡献 \(R_{u,i}\)。因为树高很小,所以信息量是可以接受的。对于 \(L\) 可以视作一个二维平面,横坐标是 \(x\),纵坐标是 \(\text{lis}\) 长度,可以树状数组优化转移。时间复杂度 \(\mathcal O(m\log^2n)\)。另一种做法就是考虑消去 \(k\) 的限制。按照 \(k\) 从大到小插入,动态维护这个线段树的信息,对于 \(\text{lca}\) 本身的方案数拆下贡献即可。

QOJ7854

考虑把点放到圆上,边视作弦,那么就要求弦只在端点处相交,也就是说一条弦可以直接分割成内外独立的两个部分。钦定任意一个节点作为圆上的首个节点,之后乘 \(n\) 即可,放在圆的位置 \(p_i\) 就是其映射到的标号。考虑树的部分分,不妨设 \(p_1=1\),我们断言一个子树对应的是环上一段连续的区间,因此对于一个点相邻的所有边都可以确定与其相接的边的顺序,答案为 \(n\prod \text{deg}_i!\)。再考虑基环树的部分分,对于一个环而言它的相对顺序一定不会变的,交换会产生逆序对状物从而导致相交,同时环上挂的子树同样一定是环上的连续区间。如果原图是一个环,答案就是 \(2n\),考虑拓展。此时这个形态与无向图上环的关系比较大,因此可以考虑缩掉点双,建立圆方树。对于基环树的情况,我们只关心一个圆点相接的所有方点的相对顺序,将一条割边的两个点视作一个单独的点双也是可以的。此时的答案就是 \(2n\prod\text{deg}_i!\),其中 \(\text{deg}_i\) 表示在圆方树上圆点 \(i\) 的度数。考虑取模前答案 \(>0\) 的情况:对于一个点双连通分量而言,可以找出来一个环,满足这个环是极大的,那么此时我们断言不存在一个点不在这个环上,否则会出现一个点 \(u\) 连接了环上两个不同的点,此时 \(u\) 的两个边放到圆上时是必然产生交点的,对于这个极大环只有 \(2\) 种,有解时的总答案就是 \(2^cn\prod{\text{deg}}_i!\)\(c\) 为大小大于 \(2\) 的点双个数,即度数 \(>2\) 的方点个数。考虑如何判断有解,我们依然只需要考虑每个点双:判定是否有一个环包含了所有节点,并且环上的弦一定是不相交的。可以用广义串并联图方法,缩二度点过程中维护所有边,在此过程维护这个环的边。使用 std::map 维护的复杂度是 \(\mathcal O(m\log n)\)

QOJ9604

显然子区间问题可以考虑分治,每次解决 \(l\in[L,mid],r\in(mid,R]\) 的所有区间 \([l,r]\) 的问题。有一个扫描线值域,维护当前最大值到中点的距离 \(p,q\) 的做法,此时可以用一个四维向量维护,因为对 \(p,q\) 的更新形如区间取 \(\min\),所以需要用到 \(\text{segbeats}\) 套矩阵维护,时间复杂度 \(\mathcal O(n\log^2n)\)。优化这个做法,同样考虑扫描线值域,\(a_i\leq x\) 的限制就转化为逐个更新。然后加入一个值 \(x\) 时就统计有多少个区间的最大值为 \(a_i\)。显然只有左侧区间的后缀最大值以及右侧的前缀最大值是有用的。对于快速合并两个有序序列可以做到单层 \(\mathcal O(r-l)\),扫描线加点是可以接受的,并且这些前缀最大值,后缀最大值可以用类似于单调栈的思想做到单层线性维护,这是因为一个点只会被一个唯一的点弹出一次。扫描线的维护值应该为 \(sum\) 表示当前所有区间的最大值之和。左右两侧的贡献与变化基本是对称的,不妨考虑加入的点在左边的做法:令左侧的后缀最大值位置分别为 \(P_0=l-1,P_1<P_2<\cdots<P_{x}=mid\),右侧的前缀最大值位置为 \(mid+1=Q_1<Q_2<\cdots<Q_{y}<Q_{y+1}=r\)。那么对于一个左侧后缀最大值而言,其贡献应该为 \(a_{P_i}(P_i-P_{i-1})(tr_{a_i}-mid-1)\),其中 \(tr_x\) 表示第一个右侧前缀最大值 \(>x\) 的位置。那么对于 \((P_i-P_{i-1})\),在动态插入删除单调栈时当然是可以维护的,问题在于右边的部分。实现结构应该是,先删去原有贡献,加上新贡献,更新右侧的 \(tr\)。考虑 \(tr\) 是有单调性的,并且是存在颜色段的,我们希望快速合并,也就是区间 \(\text{chkmax}\) 可以改成合并,统计长度,因此这个右边第一个比它大的位置可以用并查集维护,具体的,把贡献挂在 \(tr\) 上然后维护挂在它上面的 \(a_{P_i}(P_i-P_{i-1})\) 的和即可。时间复杂度 \(T(n)=2T(n/2)+\mathcal O(n\alpha(n))=\mathcal O(n\log n\alpha (n))\)

NOI2024 D2T2

显然要考虑 dp,最终需要求出的是 \(f_i\) 表示 \(i\to 1\) 的方案数,初始有 \(f_1=1\)。方便起见,令 \(h_u\) 的值表示 \(d_u-h'_u-1\)\(h'\) 是题面中读入的原始值。转移只需要考虑对第一次向上冲刺的位置进行转移,枚举 \(u\) 子树中的点 \(v\) 表示向下 \(u\) 休息到了 \(v\),枚举 \(u\) 的祖先之一 \(i\),如果满足 \(d_v-d_i\in[l_v,r_v]\)\(d_i\leq \min_{j\in\text{path}(u,v)}h_j\) 就需要有 \(f_i\) 转移到 \(f_u\),不难通过枚举 \(v\) 并处理前缀和做到 \(\mathcal O(n^2)\)

可以感受到,正解是几乎在线的数据结构优化问题,问题在于维护出所有 \(f_i\) 的系数 \(coef_i\) 使得 \(f_u=\sum coef_if_i\)。其中 \(coef_i\) 的意义就表示 \(u\) 子树有多少个 \(v\) 使得 \(f_i\to f_u\) 的转移是合法的。那么子树中 \(l_v,r_v\) 就相当于链 \(+1\),但是 \(h\) 让问题复杂了起来。为了去掉与区间 \([l,r]\) 的交集,我们尝试将更新改为对于 \(r_v+1\) 以上的 \(coef\) 进行 \(-1\),对于 \(l_v\) 以上的进行 \(+1\)\(h\) 的限制对两个操作独立,分别是 \(+1\)\(-1\)。具体的,我们考虑 \(u\to1\) 的链,从前往后是深度为 \(0,1,\cdots,d_u\) 的点,构成一个长度为 \(d_u+1\) 的序列 \(s\)\(s\) 序列的每个元素是 \(h_i\),由不同的 \(R\in\text{anc}(u)\) 表示当前考虑到 \(f_R\),就会有不同的区间的 \(coef\) 被重置为 \(0\)。先考虑这个序列 \(s\),一个结论是我们只会关心所有的后缀最小值位置集合 \(P\),当找到 \(R\) 时,我们只关心 \(R\) 在这个序列中向后大于等于它的首个 \(P\) 中的元素,因此我们可以构造一个重构树,\(u\) 的父亲 \(up_u\) 表示 \(u\) 的祖先中最深的 \(v\) 使得 \(h_v<h_u\),显然这相当于跳了若干级祖先,重构树上的边代表着原树的一条直链,更新理应是链加。对于这两个限制可以分开考虑,不妨看到 \([0,p]\) 的操作 \(+1\),那么对于某个 \(u\) 其造成的就是 \([h_{\text{next}_u}+1,p]\)。所以我们可以找到 \(P\) 中首个 \(h<p\) 的位置打上 \([h+1,p]\) 的标记,后续的 \(\min\) 更新操作就与 \(p\) 无关了,给 \(h\) 打一个 tag 然后在重构树 dfs 时累加一下这个标记即可。后续的颜色视为 \(P\) 上那个点的即可。

现在的操作 \((x,y,w)\) 就是形如维护祖先序列中的 \(w\sum_{x\leq i\leq y}f_i\),可以使用差分的思想,差分成 \(x-1\) 处以及 \(y\) 处对于树上前缀和的修改,系数分别为 \(-w,w\)。这个操作赋上颜色 \(u\) 表示这是休息点为 \(u\) 的贡献,每次 \(f\) 只需要询问所有子树中颜色的 \(f\) 之和。树状数组维护,时间复杂度 \(\mathcal O(n\log n)\)

P6630

考虑一个点存在标记的概率为 \(f_o\),如果被 pushdown 到标记就要求其祖先中存在标记并且寻找线段划分的路径过程中可以经过它的父亲,因此需要维护一个祖先中存在至少一个点有标记的概率 \(g_o\)。最终答案为 \(\sum f_o\),由于 \(k\) 很大,所以可以猜到这是一个矩阵快速幂的题。先在递归过程中考虑一下,考虑将节点区间 \([l,r]\) 与修改区间的关系分类:无交,\([l,r]\subseteq [ql,qr]\),剩余均是相交但不包含,但是还需要考虑父亲区间 \(I\)\([ql,qr]\) 的关系,这是因为一个区间尽管无交,但是其父亲如果获得标记且继续往儿子 pushdown 的话当前区间也会获得标记。因此需要考虑父亲区间 \(I\)\([ql,qr]\) 的关系。重新考虑是 \(I\subseteq [ql,qr]\) 时会令 \(g_o'=1\)\(I\)\([ql,qr]\) 无交集 \(o\) 的信息就不变;\(I\)\([ql,qr]\) 相交但不包含:此时如果 \([l,r]\subseteq [ql,qr]\) 那么 \(f_o'=1,g_o'=0\);如果 \([l,r]\) 无交但是 \(I\) 有交即不会递归到 \(o\),那么 \(f_o'=1-(1-f_o)(1-g_o)\) 表示减去自己和祖先上都没有标记的概率,\(g_o'=0\);如果 \([l,r]\) 仍未停止还会往下递归,那么 \(f_o'=g_o'=0\)。对于每个 \([ql,qr]\),其贡献系数都是随机到它的概率 \(1/{\binom{n+1}{2}}\),然后产生一个新的 \(f,g\)。这些都可以直接求和。注意到转移形式与 \(k\) 以及操作次数无关,那么可考虑矩阵快速幂优化。在一次转移中,新的 \(f,g\) 可以由原来的 \(f',g'\) 由一个线性系数求出,分别表示五种不同的转移各自发生的概率 \(p_1,p_2,p_3,p_4,p_5\),给定 \([l,r]\) 以及 \(I\) 时都是可以求的。时间复杂度 \(\mathcal O(nT^3\log k)\),其中 \(T=4\) 表示矩阵的大小。合并一下位置可以做到 \(T=3\)

QOJ9611

考虑这个式子直接做是挂掉的捏。进行一些转化:令 \(\prod x_i=\sum_{\text{sequence }t,t_i\in[1,+\infty]}\sum[t_i\leq x_i]\)。这样一个数列会在 \(t_i\leq x_i\) 产生 \(1\) 的贡献,合计贡献就是 \(\prod x_i\)。问题转化为,对于所有长度为 \(n\)\(x_i\in[1,n]\) 的序列,计数有多少 \(m\)\(n\) 列的矩阵,每行是一个 \(n\) 的排列且有 \(q\) 个位置给定,求所有 \(p_{j,i}\ge x_i\) 的方案数的和。考虑给定 \(x\)\(q=0\) 怎么计算,每列的确定在哪个位置并不重要,不妨设 \(x_i\) 是从大到小排序的,那么直接从大到小填一下就可以了,大概就是一个乘积式。然后考虑不固定 \(x\),进行统一求和,设计一个 dp 表示 \(f_{i,j}\) 为有多少个 \(m\times j\) 的矩阵满足这些 \(x>i\),此时的所有方案数和记为 \(f_{i,j}\)。初始状态为 \(f_{n,0}=1\),状态合法当且仅当 \(n-i\ge j\),转移为 \(f_{i,j}\binom{k}{j}(\frac{(n-i-j+1)!}{(n-i-k+1)!})^m\to f_{i-1,k}\),意义为填最后的 \(k-j\) 列方案数以及选取前面 \(j\) 列的方案数之积。\(q=0\) 直接做是 \(\mathcal O(n^3\log m)\) 的。考虑 \(m\) 是很大的,但是 \(q\) 极小。令存在 \(q\) 个确定点的行列是关键的。因此可以去除掉这 \(q\) 个点之后对剩余的行列进行平凡 dp,但是 \(q\le 10\) 就考虑 \(2^q\) 的东西,给原先的 \(q=0\) 上加个状压。那么就是 \(f_{i,j,S}\) 表示有多少个 \(m\times (j+|S|)\) 的矩阵,满足 \(x>i\) 且考虑了 \(j\) 的平凡列以及 \(S\) 集合中的关键列的方案数。一个必须要考虑的系数就是 \(\binom{k}{j}\) 表示选取这些非关键列的方案数。那么就考虑一共有 \(c\) 个关键行,对于非关键行的系数显然就是 \((\frac{(n-i-j-|S|+1)!}{(n-i-k-|T|+1)!})^{m-c}\)。对于这 \(c\) 个关键行可以暴力考虑,预处理一个 \(g_{p,i,S}\) 表示对于第 \(p\) 个关键行有多少个定点不属于 \(S\),且这个值 \(\ge i\)。转移就是 \(\frac{(n-j-g_{p,S,i}-i+1)!}{(n-k-|T|-g_{p,T,i}-i+1)!}\)。注意到转移系数中的 \(S,T\) 之间没有联系,因此可以分别计算系数。枚举 \(k\) 后进行高维前缀和优化,时间复杂度 \(\mathcal O(n^32^qq)\),可以预处理快速幂消掉一个 \(\log m\)

AT_wtf22_day1_c

牛牛牛。

考虑对树黑白染色,那么每次删除的就是两个异色叶子。先考虑删干净之后是什么形态:一堆不交的子树会被删掉,剩下的是一个连通块。放到每个子树上分析,就是要对里面做一个完美匹配,但是如果要删干净可能会有异子树。维护一个 \(cW_u,cB_u\) 分别表示子树内黑点以及白点的个数。显然一个方案可能存在一个子树内不完全匹配,剩下的这些点就需要和子树外面的点去进行匹配。感性理解一下,一个子树内需要完全匹配就需要往外面去借 \(T_u\) 个点。那么显然对于每个 \(u\)\(T_u\) 是存在一个下界的。然后考虑可行的与外面匹配的点就是 \(T_u,T_u+2,T_u+4,\cdots,\text{size}(u)\),需要同奇偶性的。对于 \(T_u\),考虑先是子树内贪心匹配掉一些,那么剩下的叶子一定都是同色的,进一步,不妨设 \(cW_u>cB_u\),那么所有叶子都是白色的,要想进一步消就要去借黑色节点来消掉白色叶子。不断重复操作直到 \(cW=cB\),会由外面借来 \(cW_u-cB_u\) 个黑色节点。此时,我们还是子树内能匹配就删,当删不了时,这个子树剩下的是一条从根向下的链需要借外面的点,我们贪心地想要最小化这个链的长度。\(f_u\) 就是链的长度加上 \(cW_u- cB_u\)

先不考虑树删干净的情况,考虑给未删除部分随便定一个根,那么删除的部分就可以刻画为若干不交子树 \(t_1\sim t_k\),考虑找一下必要条件:根节点颜色不全相同,\(\sum cW_u=\sum cB_u\)\(f_u+\text{size}_u\leq \sum \text{size}_i\)。这个是充分的,构造考虑找两个 \(cW>cB\)\(cB<cW\) 的做匹配,然后可以归纳满足条件 \(3\),可以发现消完之后就剩下了一堆链,可以去满足条件 \(1,2\)。 有了这三个条件,去求 \(f_u\) 也是容易的。

计数就考虑枚举 \(\sum \text{size}_i\),然后在树上二维背包 \(\text{dp}\),记录黑色节点和白色节点个数、根是否颜色相同。求答案时枚举删除的节点中深度最浅的 \(u\),那 \(u\) 的子树补也是被删除的,换根一下就好了。时间复杂度 \(\mathcal O(n^5)\),明显是跑不满的。

联合省选 2024 D2T1

又看了一遍,做法忘光了,竟然自己会了。

字典序首位的最大化是最强的限制。当前在 \(u\) 的话,作为 Alice,我们可以剪掉一些右子树使得剩下可以到达的叶子节点中的最小值最大。看到这个限制可以直接二分表示是否可以做到 \(\ge x\)。但是需要对所有 \(x\) 求的话就爆了。考虑 \(\text{dp}\),对于每个子树 \(u\) 维护 \(f_{u,i}\) 表示仅考虑 \(u\) 子树内的开关,第一步要到达 \(u\) 子树内叶子节点 \(q\) 的第 \(i\) 小的最小代价,同时维护一个 \(T_{u,i}\) 表示这个具体是谁,对于子树合并使用归并排序可以做到 \(\mathcal O(2^nn)\)。后面我们考虑直接模拟 Bob 的行走,然后相当于确定了首位 \(v\),那么考虑 Alice 最大化剩下的部分。考虑 \(v\) 到根的路径提出来,从底到根挂着的子树就是要依次求解的。注意到 \(f\) 只能帮助我们判断是否在最坏情况是合法的,给出一个代价的下界,具体决策还是要自己在模拟过程中去做的。相当于 \(f\) 是一个虚的东西,但是你每次做操作是实际发生的。那么从这条到根路径向下走到挂着的子树时,我们要为后面预留一些代价。考虑这个子树内的首位为 \(t\),如果 \(t<v\) 且当前子树的根是 \(u'\),如果 \(u'\) 的身份是一个右子树即 \(u'\) 为奇数,就必须要开启 \(a_{\text{fa}(u')}\),不然就会与预想的先走 \(v\) 冲突。此时实际的代价限制是 \([t<v\wedge 2\not\mid u']a_{\text{fa}(u')}+f_{u',\text{rank}(t)}+B\leq f_{u,\text{rank}(v)}\),其中 \(B\) 表示走出这个子树 \(u\) 后至少要剩下多少代价。那么问题在于怎么求出 \(B\)。这个直接对于 \(f_{u,v}\) 考虑它是怎么来的,直接减去这个子树的贡献,那么向下递归时剩下的就是一个路径后缀的限制,显然是对的。时间复杂度 \(\mathcal O(2^nn)\)

UOJ840

字典序可以考虑调整法进行分析。 难点在于判定 \(S\) 是否合法。

考虑计数,注意到一个显然的事情:最后一个 \(c=1\) 的真实值必然是 \(n\)\(S\) 是否包含这个并无所谓。这启示我们按照当前前缀中最后一个未被确定的 \(c_i=1\) 来划分状态。而对于 \(c=0\) 的位置一定是单调递增的,否则考虑交换后字典序不劣。分析前缀最大值的情况,考虑一个必要条件:对于所有 \(c_i=1\)\(i\in S\) 的所有 \(i\) 构成的集合 \(T\subseteq S\),对于任意的 \(i<j,i\in T,j\in S\),如果 \(b_i>b_j\) 且交换 \(b_i,b_j\) 生成的 \(c\) 方案仍然与输入的 \(c\) 相同,那么这样的 \(S\) 一定是非法的,否则交换后字典序是不劣的。现在考虑 \(b\) 能否是字典序最小的方案,依然对于 \(c_i=1\)\(i\) 分析,令下一个前缀最大值的位置是 \(nxt_i\),不存在则为 \(n+1\)。那么相当于要求 \([1,nxt_i)\) 的最大值为 \(b_i\)。 现在要求 \(j\in S,i<j,b_i>b_j\) 交换后不符合条件,即生成的 \(c\) 不同。自然想到考虑 \([1,nxt_i)\)大值为 \(k\),注意到 \(b_j\ge b_{nxt_i}\) 或者 \(b_j\leq k\) 都是无用的,会导致 \(c\) 的改变。那么考虑当且仅当 \(k<b_j<b_{nxt_i}\)\(j\) 可以与 \(i\) 交换且不改变 \(c\) 的取值。也就是说,对于某个前缀最大值 \(i\),如果 \(i\in S\),就可以推出所有 \(k<b_j<b_{nxt_i}\)\(j\) 必须满足 \(j\notin S\)。用构造的思想,这个条件是充分的,每次可以放的最小值一定是符合条件的。

考虑计数,这个是在值域上,对于每个前缀最大值而言就会限制若干两两不交的值域区间 \((l_i,r_i)\) 表示如果 \(c_i=1,i\in S\),就必须有此值域区间的 \(b_j\in(l_i,r_i)\) 满足 \(j\not\in S\),这些值域区间以及限制是互不干扰的。注意到 \(c\) 是关键的,之前说了 \(c=0\) 的位置必然单调递增,所以可以考虑对 \(c\)\(0\) 的位置做 dp。设计 \(f_i\) 表示 \([1,i]\) 中选取 \(S\)\(i\in S,c_i=0\) 的方案数。转移考虑上一个为 \(c_j=0,j\in S\)\(b_j<b_i\) 的进行转移,转移有 \(f_j2^{w(b_j,b_i)}[b_j<b_i]\to f_i\)。其中 \(w(L,R)\) 表示有多少限制区间 \((l_i,r_i)\)\([L,R]\) 完整包含,表示这些未决策的 \(b\) 会与对应的前缀最大值位置一起做决策,系数是 \(\times 2\),暴力可以做到 \(\mathcal O(n^2)\)。而限制区间不交,所以可以树状数组优化转移,时间复杂度 \(\mathcal O(n\log n)\)

NOI2024 D1T3

特殊性质 \(\text{A}\) 是合法解有且仅有 \(2\) 组(奇数层外向,偶数层内向);特殊性质 \(\text{B}\)\(2-\text{SAT}\) 问题的字典序最小解。

考虑确定一个前缀的定向之后判定之后是否存在合法方案。现在删去已经合法的路径(即路径上存在反向边)之后,限制是所有路径中,已经确定的边的方向均与路径方向相同的路径,那么就形如当前一条路径的子集 \(S\subseteq E\) 中(\(S\) 是没有确定放方向的边的集合)至少存在一条反向边。如果 \(|S|=0\) 就是显然无解的,因为这条路径无法被调整并且已经寄了,否则考虑 \(|S|=1\) 的是可以直接对 \(S\) 中唯一的元素进行定向的,不断缩直到不存在 \(|S|=1\) 或者宣告方案是不合法或者合法的。考虑在不存在 \(|S|=1\) 时,由特殊性质 \(\text{A}\) 进行二染色必然有解,我们断言一个结论:如果当前未确定合法的路径中,其中未定向的边集合 \(S\) 均满足 \(|S|>1\) 时,这种情况是一定有解的,因为这个限制是弱于性质 \(\text{A}\) 的。直接暴力做的复杂度是 \(\mathcal O(nm^2)\) 的,结合性质 \(\text{AB}\) 期望获得 \(48\) 分。现在瓶颈在于快速缩掉所有 \(|S|=1\) 的路径。可以考虑在确定边的方向同时去缩掉 \(|S|=1\) 的路径,使用堆以及队列等数据结构优化寻找的过程可以做到 \(\mathcal O(nm\log m)\),期望获得 \(64\) 分。

进一步优化的方向就是维护这个 \(|S|=1\) 的队列。瓶颈在于如何找到 \(|S|=1\)。考虑一个报警器的 trick,可以将一个路径拆到若干链上,然后数据结构维护这些链,当链上未被定向的边数达到 \(0,1\) 时分别报警一次。然后寻找的复杂度就是拆成 \(k\) 条链,我们希望在 \(\mathcal O(\text{poly}(k))\) 的复杂度内找到。对于这个拆链,点分治,倍增,树剖都是可行的。树剖做法需要拆成 \(\mathcal O(\log n)\) 段轻重链剖分上的区间,每个还需要在线段树上 \(\mathcal O(\log n)\) 定位,这样的复杂度是 \(\mathcal O((n+m)\log^2n)\)。对于倍增做法,考虑拆成 \(u\) 到其 \(2^i\) 级祖先的路径,那么朴素做法中需要暴力维护的,对于每条边,每条覆盖其的路径,可以优化到直接往这些倍增路径去连边。然后对于每个限制拆成 \(\mathcal O(1)\)\(u\to\text{lca},\text{lca}\to v\) 的直链。对于每条倍增区间,需要维护它的信息:确定的边中,不存在被确定的,均为内向,均为外向,方向混合;同时记录未确定方向的边的个数,如果是恰好 \(=1\) 的就需要记录它的编号。容易发现,单次确定某条边的方向之后就可以 \(\mathcal O(\log n)\) 更新,然后做一遍类似拓扑排序的东西来维护报警过程即可。然后对于一个倍增区间,向合并它的两个部分连边边数就是正确的 \(\mathcal O((n+m)\log n)\),这样就可以直接用 \(O(\log n)\) 的复杂度处理,而每个倍增区间只会被更新 \(\mathcal O(1)\) 次。最终总时间复杂度 \(\mathcal O((n+m)\log n)\)。另种思路同样是倍增,但是考虑树上带权并查集维护缩边过程。常数很大,LOJ 和 UOJ 分别只能获得 \(88\) 分以及 \(84\) 分。

CF1987F2

显然是一个区间 dp 的形式,但不是对答案去设计状态,最终没法合并的。考虑对一个数列维护当前的 \(b_i=a_i-i\),操作会让 \(i\) 是变的,每次选取一个 \(b_i=0\) 的位置删去 \((i,i+1)\),然后对于后缀 \([i+2,|a|]\)\(b_j\) 增加 \(2\)。并且我们考虑刻画最终数列长什么样子,将删除的两个数连一条边,看成区间的话就是一个【不相交仅包含/相离】的结构,套路化地对于相交(包含关系)的区间考虑最外面那个区间 \([l,r]\),连续的你可以黏在一起,发现我们只需要判定 \([l,r]\) 是否可以删空。但是朴素的状态完全无法描述,瓶颈在于需要另一维来体现向前位移的可行性。为了避免做无用功,最好想想怎么统计答案。假设已经求出了一段前缀的答案 \(ans_i\),你现在想要加上一个区间 \([i+1,j]\) 被删空然后 \(ans_i+\frac{j-i}{2}\) 转移到 \(ans_j\),你考虑 \([i+1,j]\) 内部的操作与前缀的操作是互相不关联的,但是需要考虑 \([i+1,j]\) 在向前移位 \(k\leq ans_i\) 时是否可以删空。因此有一个唐诗的状态是 \(f_{l,r,k}\in\lbrace0,1\rbrace\) 表示 \([l,r]\)\(b\) 整体增加 \(2k\) 时是否可以删空。然后注意到可行的 \(k\) 事实上是一段后缀,因为你可以调整前面与后面操作的顺序。因此 dp 状态就是 \(f_{l,r}\) 表示区间 \([l,r]\) 至少向前移位多少才能被删空。这时考虑我上面说的那个图论模型,一种情况是 \(l\)\(k\) 连边,\(k+1\)\(r\) 连边,其中 \(l<k,k+1<r\),转移为 \(\max(f_{l,k},f_{k+1,r}-\frac{k-l+1}{2})\),另一种是考虑 \(l\)\(r\) 连边,判定 \(a_l\) 转化后的值与 \(f_{l+1,r-1}\) 即可。

时间复杂度 \(O(n^3)\),瓶颈在于枚举断点求 \(f\)

posted @   nullptr_qwq  阅读(60)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!
点击右上角即可分享
微信分享提示