2026.7 杂题
P16958 [SCCPC 2026] 括号序列
以下的 \(n\) 不是字符串长度,而是括号的对数,也就是字符串长度的一半。
转化成一个网格图,每一次只能向上或向右走,且不能走到 \(y=x+1\) 这条直线上。对于给定路径 \(X\),计算有多少条从 \((0,0)\to (n,n)\) 的路径 \(Y\) 满足:考虑 \(Y\) 上第一个不在 \(X\) 上的点 \(a\),则 \(a\) 在路径 \(X\) 上方。
定义 \(G(x,y)\) 为点 \((x,y)\) 出发,只往上或往右走,走到 \(y=x\) 且 \(x\le n\) 且不经过 \(y=x+1\) 的方案数。于是考虑枚举 \(X\) 每一个横着的极长的一段,也就是从 \((l,y)\to (r,y)\) 的一段,那么贡献就是 \(G(l,y)-G(r,y)\)。
而 \(G(x,y)=\sum_{i=0}^n {2n-x-y\choose n-x}-{2n-x-y\choose n-x+1}\),于是可以很轻松地 \(O(n^2)\) 计算。
考虑如何优化:设 \(f(m,d)=\sum_{i=0}^m{2i+d\choose i}\),那么
然后考虑如何求 \(f(m,d)\)。我们发现这个形式根本不好写成通项,于是考虑找递推式:
然后考虑 \(d\) 的变化:
然后通过这个等式,我们如果知道 \(f(m,d)\) 和 \(f(m,d+1)\),就可以 \(O(1)\) 计算出 \(f(m,d+2)\) 和 \(f(m,d-1)\)。于是维护两个指针 \(m,d\) 和值 \(\{f(m,d),f(m,d+1)\}\),当指针变化时维护的值也跟着变化。可以发现指针的移动次数是 \(O(n)\) 的,于是时间复杂度为 \(O(n)\)。
P8987 [北大集训 2021] 简单数据结构
基本上算是自己做的吧。
可以发现,我们每一次只看被 \(\min\) 操作修改过的数,它们一定是单调不降的。所以每一次 \(\min\) 操作都可以直接用二分+覆盖操作解决。这一部分可以用平衡树快速维护,内部维护值就是一个一次函数,同时维护斜率和截距即可。
然后考虑计算每一个位置第一次被 \(\min\) 操作修改的时间呢?如果位置 \(i\) 会被第 \(j\) 次操作修改,设 \(tm_j\) 为第 \(1\sim j\) 次操作中有多少次是全局 \(+i\),那么一定满足: \(a_i+tm_j\times i\ge v_j\)。重新换一种写法则有:\(a_i\ge v_j+(-tm_j)\times i\),于是就是一个一次函数的形式。于是考虑整体二分,然后二分中间用李超树/单调栈去维护下凸壳即可。
时间复杂度为 \(O(n\log ^2n)\)(李超树)。如果单调栈维护就是 \(O(n\log n)\)。
P4887 【模板】莫队二次离线 / 第十四分块(前体)
莫队二次离线使用的一些特征:
- 指针的挪动不是 \(O(1)\) 的。
- 每个新增的数的贡献和当前的区间有关。
通常有以上的特征时我们就可以考虑把中间的一些东西离线下来用扫描线单独求。
比如说这道题,我们在指针挪动时,时间复杂度是一个很大的数,且 \(x\) 的贡献和当前区间 \([l,r]\) 有很大关系,所以考虑莫队二次离线。
设 \(f(x,l,r)\) 为在区间 \([l,r]\) 中,\(a_x\) 与 \(a_{l\sim r}\) 的贡献之和。我们考虑移动左右指针时答案的变化:
-
将右指针从 \(r\) 挪到 \(R\)
这里讨论 \(r<R\) 的情况,也就是右指针右移时。左移情况本质相同。
此时答案的变化应该是 \(\sum_{i=r+1}^R f(i,l,i-1)=\sum_{i=r+1}^R f(i,1,i-1)-f(i,1,l-1)\)。所以记录 \(F(x,d)=f(x,1,d)\),那么贡献的变化就是 \(\sum_{i=r+1}^R F(i,i-1)-F(i,l-1)=\sum_{i=r+1}^R F(i,i-1)-\sum_{i=r+1}^R F(i,l-1)\)。其中 \(\sum_{i=r+1}^R F(i,i-1)\) 可以直接提前预处理然后用前缀和快速求出。对于后面那项我们考虑先用
vector将其作为一个五元组存下来,参数是 \((d,l,r,\pm 1,i)\) 表示让第 \(i\) 个询问的答案加/减上 \(\sum_{i=l}^rF(i,d)\)。 -
将左指针从 \(l\) 挪到 \(L\)
这里讨论 \(l>L\) 的情况,也就是左指针左移时。右移情况本质相同。
此时答案的变化应该是 \(\sum_{i=L}^{l-1}f(i,i+1,r)=\sum_{i=L}^{l-1}F(i,r)-F(i,i+1)=\sum_{i=L}^{l-1}F(i,r)-\sum_{i=L}^{l-1}F(i,i+1)\)。后面那项一样可以用前缀和预处理出来。前面的那项,同样用
vector存下五元组即可。
这样五元组的个数是 \(O(q)\) 的,所有五元组的 \(r-l\) 之和为 \(O(q\sqrt n)\) 的。
于是考虑用扫描线计算出五元组的贡献。将所有五元组按 \(d\) 的大小从小到大排序,然后考虑如何快速计算。注意到值域很小,只有 \(2^{14}\),因为 \(A\oplus B=C\) 则 \(A\oplus C=B\)。于是对于 \(d\to d+1\) 时,对于所有二进制下拥有 \(k\) 位 \(1\) 的数 \(x\),则所有 \(a_d\oplus x\) 的数的贡献加一。所以开个桶记录下所有数的贡献。然后对于一个五元组,直接从 \(l\) 扫到 \(r\) 依次计算每一个数的贡献,因为 \(r-l\) 之和为 \(O(q\sqrt n)\) 且每一次贡献计算是 \(O(1)\)。于是时间复杂度为 \(O(q\sqrt n+n{14\choose k})\)。
P14384 [JOISC 2017] 烟花棒 / Sparklers
神仙题目。
注意到可以二分答案速度 \(s\)。考虑怎么判断是否可行。
设中间人的位置是 \(p\)。
首先观察怎么走更优。注意到所有未被点燃的人一定是向 \(p\) 跑的,且越快越好,所以速度为 \(s\),速度一样,所以说所有位于 \(p\) 左侧未被点燃的人之间的相对距离是不变的, \(p\) 右侧未被点燃的人也同理。然后观察拿着点燃的烟花棒的人该怎么做,为了时间最短,他一定朝着他下一个传递的目标以速度 \(s\) 移动,直到和那个人相遇,在此期间,所有相邻的两个人之间的距离中只有这两个人之间的距离在以 \(2s\) 单位每秒的速度减少。然后考虑他们这两个人相遇后该怎么做,现在有两种选择:
- 直接把火传递给下一个人,然后两个人分别回头分别向两侧没被点燃的人跑去。
- 为了避免烟花的浪费,让他们两个人一起运动,当第一个人的烟花燃的最后一刻传递给下一个人,于是可以把这两个人看成同一个人,然后烟花燃烧时间 \(+T\)。
你会发现第一种反而会浪费烟花。因为在移动的过程中除了相向而行的人,其他同向而行的人之间的距离是不变的,所以任意相邻的两个人相向而行传递的时间都是他们之间的距离除以 \(2s\)。那么如果你此时拥有的烟花时间更长,你就更有机会成功传递。所以选择第二种保留烟花的做法是更优的。
此时刻画一下这个问题,就相当于每一次中间的人 \(p\) 选择左右相邻的其中一个人 \(u\) 传递,那么这两人相向跑去,消耗的烟花是 \(\frac{|x_p-x_u|}{2s}\),然后烟花的存量 \(+T\),把这两个人合并成一个人。重复这个操作直到所有的人都被合并了。
那么就相当于维护了一个 \(l,r\) 初始都等于 \(p\),然后每一次 \(l\gets l-1\) 或 \(r\gets r+1\),让烟花存量减去新加入的这一段的距离除以 \(2s\),再加上 \(T\)。一种将 \(l\) 挪到 \(1\),\(r\) 挪到 \(n\) 的方式合法当且仅当任意时刻烟花存量都是非负的,也就是任意时刻 \((r-l)T\ge \frac{(x_r-x_l)}{2s}\)。将这个不等式简化一下,则有 \(2Tsl-x_l\le 2Tsr-x_r\)。设 \(c_i=2Tsi-x_i\),则满足所有的 \(c_l\le c_r\)。
我们把 \(c_{1\sim p}\) 和 \(c_{p\sim n}\) 分割成两个数组 \(a,b\),然后把第一个数组 \(a\) 翻转一下,再把所有值都减 \(1\)。那么这个问题就是 双序列拓展 了啊。复习一下做法,将 \((l,r)\) 视作一个二维平面上的坐标,然后从 \((1,1)\) 出发每一次向上或向右走一步一直走到 \((s,t)\)(其中 \(s,t\) 分别是 \(a,b\) 数组的长度),每一个格子 \((l,r)\) 可走当且仅当 \(a_l< a_r\)。
先找到 \(a\) 中的最小值所在位置 \(X\) 和 \(b\) 中最大值所在位置 \(Y\)。若 \(a\) 最大值 \(\ge b_Y\) 或 \(b\) 的最小值 \(\le a_X\) 则一定无解。否则 \(x=X\) 和 \(y=Y\) 这一行一列一定全部可走。然后整个问题分为了两个子问题:判断是否可以从 \((1,1)\) 走到 \(x=X-1\) 或 \(y=Y-1\);判断是否可以从 \(x=X+1\) 或 \(y=Y+1\) 走到 \((s,t)\)。
-
对于第一个子问题:给定 \(L\) 和 \(R\),判断是否可以从 \((1,1)\) 走到 \(x=L\) 或 \(y=R\)。
找到 \(a\) 的区间 \([1,L]\) 最小值所在位置 \(X\),和 \(b\) 的区间 \([1,R]\) 在这一部分的最大值所在位置 \(Y\)。若 \(a\) 在区间 \([1,L]\) 的最大值 \(\ge b_Y\) 且 \(b\) 在区间 \([1,R]\) 的最小值 \(\le a_X\) 则无解。否则可以进一步缩小 \(L,R\) 的范围,可以递归求解。
若 \(\min(L,R)\le 1\) 则说明有解。
-
对于第二个子问题:给定 \(L\) 和 \(R\),判断是否可以从 \(x=L\) 或 \(y=R\) 走到 \((s,t)\)。
找到 \(a\) 的区间 \([L,s]\) 最小值所在位置 \(X\),和 \(b\) 的区间 \([R,t]\) 在这一部分的最大值所在位置 \(Y\)。若 \(a\) 在区间 \([L,s]\) 的最大值 \(\ge b_Y\) 且 \(b\) 在区间 \([R,t]\) 的最小值 \(\le a_X\) 则无解。否则可以进一步增大 \(L,R\) 的范围。同样可以递归求解。
若 \(L\ge s\) 或 \(R\ge t\) 则说明有解。
于是对于一个 \(s\) 可以 \(O(n)\) 进行判断是否可行。那么这个题就可以在 \(O(n\log V)\) 的时间复杂度解决。
P5972 [PA 2019] Desant
首先最朴素的想法是从前往后 dp,记录哪些数被选了,这样做的状态数至少是 \(2^n\) 级别的,无法通过。
观察到有哪些状态是本质相同的。我们发现,当我们考虑了位置 \(1\sim i\) 后,设 \(i+1\sim n\) 的位置上的数排序后分别是 \(x_1,x_2,...,x_k\) 满足 \(x_1< x_2< ...< x_k\),那么如果我们知道了我们已经选了的值属于 \([0,x_1),(x_2,x_3),...(x_k,n]\) 的个数分别有多少个就可以算出后面的所有贡献。所以我们直接记录下每一个值域段有多少个数被选即可。
考虑这个东西的状态数,令 \(x_0=0,x_k+1=n+1\),则状态数显然是 \(\prod_{i=1}^{k+1} [(x_i-x_{i-1}-1)+1]=\prod_{i=1}^{k+1}(x_i-x_{i-1})\)。这个东西就相当于把 \(n\) 拆成若干个正整数相加,使得这些正整数乘积最大。考虑怎么拆:
首先不会拆出 \(\ge 4\) 的数,因为这些数都可以进一步地再拆开,且拆开后是不劣的。然后就是拆 \(2\) 还是 \(3\) 的问题了。注意到 \(n=6\) 时拆成 $3\times 3 $ 是要比 \(2\times 2\times 2\) 更大的,所以 \(2\) 的个数必然少于三个。那么拆出来的大部分都是 \(3\),于是这个最大的乘积就可以写成 \(O(3^{n/3})\)。
于是状态数是 \(O(n3^{n/3})\) 的,但是还要存 \(O(n)\) 个数,转移是 \(O(1)\) 的。所以时间复杂度是 \(O(n^23^{n/3})\)。这里用手写哈希表更快,好像拉链法比开放寻址更快。
P2282 [HNOI2003] 历史年份
口胡出了一个 \(O(Tn\sqrt n\log n)\) 的做法。但是无法通过。
设 \(f_i\) 等于最大的 \(j\) 满足存在一种合法的分割 \(1\sim i\) 的方式,使得最后一段是 \([j,i]\)。这个转移我们考虑用刷表法,考虑 \([f_i,i]\) 向后转移。定义区间 \([l,r]\) 的位数为:找到最大的 \(x\) 使得 \([l,x-1]\) 全为 \(0\),则位数为 \(r-x+1\),说白了就是去掉前导 \(0\) 后的数的位数。 那么找到后方第一个 \(r\) 满足 \([i+1,r]\) 的位数等于 \([f_i,i]\) 的位数。然后判断这两个区间的数谁更大,如果 \([f_i,i]\) 的数不小于 \([i+1,r]\),则把 \(r\) 加 \(1\)。那么 \(f_{r\sim n}\) 都可以被 \(i+1\) 更新,这是一个后缀 chkmax,所以打一个 tag 即可。转移的时间是 \(O(1)\) 的,但是问题在于判断两个区间构成的数谁更大。
然后我们在满足最后一个数尽可能小后,现在考虑如何让前面的数尽可能大。所以考虑从后往前考虑,设 \(g_i\) 表示最大的 \(j\) 满足存在一种分割 \(i\sim n\) 的方式使得第一块是 \([g_i,j]\) 且最后一块是 \([f_n,n]\)。这个转移和 \(f_n\) 类似,转移是 \(O(1)\) 的。
那么现在就该解决如何快速判断两个区间构成的数谁更大。首先观察到最终方案中每一段的位数大小是不超过 \(O(\sqrt n)\),否则可以直接第一块位数设为 \(1\),第二块位数设为 \(2\),依次类推,使得所有块的位数都是不超过 \(O(\sqrt n)\) 的。所以其实直接暴力判断是 \(O(\sqrt n)\) 的,总时间复杂度是 \(O(Tn\sqrt n)\),这个跑的很快。当然还有一些其他方法,比如说本质上这玩意儿是字典序比较,所以直接二分+哈希可以做到 \(O(Tn\log n)\),但是这个跑不过上面那个。
P7408 [JOI 2021 Final] 地牢 3 / Dungeon 3
首先考虑我们的策略是什么,对于层 \(i\),找到其后方第一个价格更小的层 \(x\),如果当前从 \(i\) 走到 \(x\) 所需的能量 \(\le U\),则加到能够走到 \(x\) 为止,否则加满到 \(U\),再走到 \(i+1\)。
考虑怎么优化这个东西。我们先考虑 \(T=n+1\) 的情况。我们首先把每一层楼给放到数轴上,定义 \(x_i=\sum_{j=1}^{i-1}a_j\)。那么我们初始的时候是在 \(x_s\),然后获得 \(1\) 单位能量可以看成是向右走了 \(1\) 个单位。我们可以把在第 \(i\) 层获得的能量用一个区间 \([l_i,r_i]\) 表示,表示在到达第 \(i\) 层时你所在的位置是 \(l_i\),获得完能量后你所在的位置是 \(r_i\)。需要满足的限制是:\(x_i\le l_i,x_{i+1}\le r_i,r_i-x_i\le U\)。
于是从后往前考虑用单调栈维护出价格递减的层,用来找到 \(i\) 后方第一个价格比它小的位置。然后考虑位置 \(i\),设当前栈顶为 \(a\),栈顶第二个元素为 \(b\),我们先默认直接从 \(x_i\) 出发走到 \(x_{a}\),所以对于任意 \(U\) 都加上代价 \(B_i(x_a-x_i)\)(注意这里我们暂时不考虑无解的情况,这个东西我们后面会单独判)。现在根据不同的 \(U\) 进行分类讨论:
-
若 \(U\le x_a-x_i\) 则什么都不做。
-
若 \(U>x_a-x_i\)
此时我们可以将部分 \(x_a\) 提供的能量替换到 \(x_i\) 提供,能替换就替换。所以替换的量是 \(\min(x_b-x_a,x_i+U-x_a)=\min(x_b,x_i+U)-x_a\)。那么再进行分类讨论:
-
若 \(x_b\le x_i+U\),即 \(x_b-x_i\le U\) 时
此时代价是 \((B_i-B_a)(x_b-x_a)\)。
-
若 \(x_i+U<x_b\),即 \(x_a-x_i<U<x_b-x_i\) 时
此时代价是 \((B_i-B_a)(x_i+U-x_a)\)。这是一个关于 \(U\) 的一次函数。
-
然后把 \(x\) 弹出。重复这个操作直到 \(B_i>B_x\)。我们发现,我们可以用线段树去维护出不同的 \(U\) 的代价之和,只需要维护一个一次函数即可。
所以我们可以解决掉 \(T=n+1\) 的问题,只需要用单调栈从后往前扫。时间复杂度为 \(O((n+q)\log n)\)。
接下来考虑没有这个 \(T=n+1\) 特殊性质怎么做。首先先把无解判掉,如果 \(\max_{i=S}^{T-1} A_i>U\) 则无解,用 st 表维护一下即可。然后考虑询问 \((S,T,U)\),找到数轴上 \([x_T-U,x_T]\) 的价格最小的位置 \(k\),那么 \(l_k=x_k,r_k\ge x_T\)。这个条件我们发现和从 \(k\) 走到 \(n+1\) 时也满足这个条件。所以可以把询问 \((S,T,U)\) 拆成 \((S,n+1,U)-(k,n+1,U)+B_k(x_T-x_k)\)。这样所有询问都是走到 \(n+1\) 的了,所以再按上面方法做即可。时间复杂度 \(O((n+q)\log n)\)。
P6881 [JOI 2020 Final] 火灾 / Fire
建立一个平面直角坐标系,纵轴为时间,横轴为位置。那么发现每一个数在这个坐标系中是一个平行四边形,且这个平行四边形的两边是平行与纵轴,另外两条边是平行于 \(y=x\) 的。然后最下方的点是在 \(y\) 轴上的。
我们考虑我们该如何对这个平行四边形进行扫描线。首先我们可以把这个平行四边形拆成三个直角三角形权值的加减,且这三个直角三角形都满足斜边斜率为 \(1\),底边在横轴上。如图(图源xtx1092515503的题解,侵删):

然后就转化为了对三角形做扫描线。我们先把询问从 \((T,l,r)\) 修改成两个前缀的差:\((T,-\infin,r)-(T,-\infin,l-1)\)。然后将询问简写成 \((x,y)\) 表示询问 \((y,-\infin,x)\)。那么就是求出这一条从 \((x,y)\) 向左的射线与三角形的交点的权值之和。我们可以把三角形也简写成 \((a,b)\) 表示这个三角形最上面的端点的坐标。
现在考虑一个三角形 \((a,b)\) 对于询问的影响:若询问的点在三角形右侧(如下图的红线),那么造成的影响是 \(w(b-y+1)\),其中 \(w\) 是三角形权值,这是一个一次函数且范围是一个矩形,所以可以转化成矩形的扫描线再做。若询问的点在三角形内部(如下图的绿线),则可以把它等效地平移到 \((x-y,0)\)(如图中的蓝色线),所以就变成一个一维问题,三角形 \((a,b)\) 对 \((x,0)\) 的贡献是 \((x-(a-b)+1)\),这也是一个一次函数。但是有一个问题就是不要把三角形右侧的点也平移下来,这样会导致贡献的多算。所以将所有询问和三角形按纵坐标从右往左加入,保证所有被三角形 \((a,b)\) 造成贡献的询问 \((x,y)\) 都有 \(a\ge x\)。

于是这样就只需要分别对于平移后的和三角形右侧的矩形分别做两次即可,每一次需要维护一个一次函数。用两个树状数组即可解决。时间复杂度 \(O((n+q)\log n)\)。
QOJ 8315 Candy Ads
模拟赛 T1,据我所知,赛时好像想到双 $\log $ 做法的人都没通过。
双 \(\log\) 还是比较简单的,我赛时的想法是:注意到 \(\log^3\) 是非常简单的,现在考虑怎么优化到 \(\log ^2\),注意到有两位的区间长度是固定的,所以考虑双指针来优化掉其中的一位,然后剩下两维用数据结构优化建图掉。但是数据结构优化建图不支持删双指针的区间变小,所以用双栈模拟队列可以解决这个问题,只是需要可持久化。时空都是 \(O(n\log^2n)\) 的,但是空间常数极大,感觉过不了所以就没写这个。不过赛后 pmd 大神用这个做法加一些常数优化过掉了。
这道题的目标是建图,然后跑 2-SAT。强连通分量除去 Tarjan 以外还有一个只用两次 dfs 就可以做的 Kosaraju 算法。具体做法就是先在原图上跑一个 dfs 并记录下后序遍历的点,然后按后序遍历的顺序从前往后在反图上跑第二次 dfs,这样遍历到的所有点都是一个强连通分量。
因为目标是建图,所以可以考虑用 bitset 建图然后用 bitset 优化 dfs。注意到 \(i,j\) 有交才会有边 \((i,j+n),(j,i+n)\),所以求出 \(l_j>r_i\) 或 \(r_j<l_i\) 或 \(x_j\not\in[x_i-w+1,x_i+w-1]\) 或 \(y_j\not\in[y_i-h+1,y_j+h-1]\) 的点集合,这个可以用 \(6\) 个 bitset 去维护做一个前后缀按位或的和即可。这 \(6\) 个 bitset 每一个大小是 \(O(nV)\) 的,其中 \(V=2000,n=50000\),算出来就是 \(10^8\)。完全开的下。
我们不用把图建出来,不然还要开一个 \(n^2\) 的 bitset。直接在每一次用的时候再计算即可。
注意在 dfs 中计算的时候不能这么写:
to=(all^(pret[d[u].l-1]|suft[d[u].r+1]|prex[d[u].x-1]|sufx[min(2001,d[u].x+w)]|prey[d[u].y-1]|sufy[min(2001,d[u].y+h)]));
因为每一次计算的时候都会创建一个临时 bitset,所以会导致栈空间爆炸。所以要这么写:
to=pret[d[u].l-1];
to|=suft[d[u].r+1];
to|=prex[d[u].x-1];
to|=sufx[min(2001,d[u].x+w)];
to|=prey[d[u].y-1];
to|=sufy[min(2001,d[u].y+h)];
to^=all;
时间复杂度是 \(O(\frac{n^2}w+m)\)。
P8175 [CEOI 2021] Tortoise
发现其实 Wilco 的做法是完全固定的,只有 Tom 的操作是不定的。所以可以看成 Tom 的目标是去尽可能多地拿糖。因为 Wilco 是从左往右走,所以 Tom 的最优策略应该是从左边的商店开始拿糖,然后一直向右拿,这样是最优的。
因为 Wilco 的速度是 Tom 的刚好一半,所以可以把所有从 \(i\to i+1\) 所耗费的时间都减去一次,也就是 Tom 拿糖只计算除去从 \(1\to n\) 以外的额外时间,需要满足在位置 \(i\) 拿最后一个糖前所消耗的额外时间 \(\le i-1\)。
我们现在考虑拿糖消耗的额外时间怎么计算。设 \(d_i\) 为商店 \(i\) 到其最近的一个空地的距离,所以拿糖的额外时间是 \(2d_i\),因为需要拿着糖去空地再回到原处。但是你会发现会有例外情况,就是在一个商店拿的最后一颗糖时,把他运到空地后可以不返回原地,而是直接走向下一个要拿糖的商店。那么设下一个要拿糖位置是 \(nxt_i\),而 \(i\) 商店右侧第一个空地位置在 \(p_i\),那么该糖占据的额外时间就是 \(\max(0,2(nxt_i-pos_i))\),注意不要把 \(i\to pos_i\) 这一段给算进去。但是我们也可以不这么做,而是向之前的糖一样,所以最后一个糖的耗费额外时间是 \(\min(2d_i,\max(0,2(nxt_i-pos_i)))\)。
现在有一个问题了,就是我怎么知道下一个拿糖的位置在哪?我们可以发现,钦定下一个拿糖的位置在 \(i\) 右侧第一个有糖的商店一定是不劣的。如果下一个拿糖的位置不是下一个有糖的位置,那么可以通过调整变成现在的样子,可以发现一定存在一个后续位置他的特殊糖果的代价是等于 \(i\) 的特殊糖果的代价,也就是走到真正的下一个拿糖的位置的代价,而那时我们可以用它替代掉我们当前的糖果。而且我们这么钦定也不会导致错误情况被考虑。
于是可以直接钦定 \(pos_i\) 是下一个有糖的位置。这样所有糖果的代价都可以被确定。位置 \(i\) 有 \(a_i-1\) 颗普通糖果和 \(1\) 颗特殊糖果,代价分别是 \(2d_i\) 和 \(\min(2d_i,\max(0,2(nxt_i-pos_i)))\)。要求最后一次在 \(i\) 取糖果时,之前的所有糖果的代价总和 \(\le i-1\)。问最多能取多少糖果。
因为所有糖果的价值都是一样的,所以能取代价小的就取代价小的。而且代价总和的限制是递增的,所以可以考虑反悔贪心。直接用大根堆去维护所有取了的糖果的代价,每一次如果代价总和没超过 \(i-1\) 就可以直接取,否则,如果堆顶的代价比当前糖果的代价大就用当前糖果去替换堆顶,如果上述都不满足,则说明这个糖果没用。
我们可以直接暴力枚举所有糖果考虑是否加入。这个时间复杂度是多少?因为代价总和不超过 \(n\),而只有 \(O(n)\) 个糖果的代价为 \(0\),剩下的都是正整数。可以发现代价为 \(d\) 的糖果最多有 \(O(\frac nd)\) 个,所以代价应该是 \(O(\sum_{d>0}\lfloor\frac nd\rfloor\log n)=O(n\log^2n)\)。
AT_xmascon24_e Embed the Tree
这个题比较朴素的做法是设 \(dp_{u,S,c}\) 表示 \(a_u=c\),\(u\) 的子树内用的 \(a\) 值构成的集合是 \(S\),的代价最小值及其方案数。这个做法可以做到 \(O(n^33^n)\)。同时发现不用在转移 \(S|T\) 的时候去枚举 \(u,v\) 的颜色 \(i,j\),这样是 \(O(n^33^n)\),我们可以提前枚举 \(i,j,T\),用数组 \(best_{i,T}\) 去存下来,这里是 \(O(n^32^n)\),然后再将 \(best\) 和 \(dp\) 合并,这里就不用枚举 \(j\) 了,所以是 \(O(n^23^n)\)。
这个做法写一些优化跑得非常快。比如说一定有 \(|S|=sz_u\),所以直接把所有大小为 \(sz_u\) 的 \(S\) 全部预处理出来。最多只有 \({18\choose 9}=48620\) 个,所以开 \(dp_{u,S,c}\) 只有 \(18\times 48620\times 18=1.5\times 10^7\) 左右,完全可以开下。
还有另外一种理论更快的做法。就是先考虑链怎么做,这是简单的,我们同样记录 \(dp_{i,S,c}\),但是我们每一次只加入一个点,所以可以轻松做到 \(O(n^32^n)\) 的时间。
现在考虑为什么树做不了,因为每一次可能加入的是一个子树,是多个点。我们可能同时需要记录多个点填的数才能算出新加入的点的贡献。我们考虑这样一个事情,设我们以 \(\{p_1,p_2,...,p_n\}\) 的顺序一个一个加入点,为了能够计算出每一个加入的点的贡献,我们需要记录已经加入的且与加入的这个点有连边的点的颜色。我们来看一下最少我们需要维护多少个点的颜色?也就是构造一个排列 \(p\) 使得存在一个最小的 \(k\),满足 \(\forall i\in[1,n]\),令 \(S=\{p_1,p_2,...,p_n\}\),满足 \(S\) 中与 \(U\backslash S\) 存在连边的点数 \(\le k\)。这个就是树的 Pathwidth,经证明,这个值是 \(k=\lfloor\log_3{\frac{2n+1}5}\rfloor+1=3\)。
于是我们记录状态就可以记录 \(dp_{i,S,C}\) 表示加入了 \(\{p_1,p_2,...,p_i\}\),用的颜色集合是 \(S\),且需要记录的点的颜色是 \(C\) 的最小值及其方案数。可以发现当 \(n\le 18\) 时,我们只需记录 \(\lfloor\log n\rfloor=4\) 个。于是状态数是 \(O(n^{\lfloor\log n\rfloor+1}2^n)\)。在这里约为 \(O(n^52^n)\)。经过实践,这个 \(n\le 18\) 时 \(k=3\),所以实际上是 \(O(n^42^n)\)。
这个 \(k\) 被称为 pathwidth,具有 pathwidth 为 \(k\) 的树的最小顶点数是 \(\frac{5\times 3^{k-2}-1}2\)。

浙公网安备 33010602011771号