NOI真题记录

NOI真题

记录一些做过的 NOI 真题。

NOI2013 向量内积

题意:有 \(n\)\(d\) 为向量,求是否有两对向量的点积是 2 或 3 的倍数。

思路:先random_shuffle一下,然后一次判断和前面的和的乘积,如果发现出现了不满足全部模起来都不为0就说明出现了答案,与前面的每一个判断一下就可以了。

NOI2017 游戏

题意:有 \(n\) 个点,每个点有三种取值,至多只有 \(d\) 个变量可以取到三种取值,其余的都只有一种不能取,还有 \(m\) 个关系限制式,问是否有解,有解输出一种方案。

思路:这一题乍一看是一个3-sat,而3-sat没有多项式解法,于是想办法转化成2-sat。

如果先不考虑 \(x\) 地图,那么可以把每一个地图可以用的两种赛车建图,然后跑2-sat。

如果考虑 \(x\) 地图,那么就暴力枚举每个地图是不使用 \(A\) 还是不使用 \(B\)

复杂度\(O((n+m)2^d)\)

NOI2014 购票

题意:一棵树,有边权,两点的路径长度为边权之和,点 \(x\) 可以到距离不超过 \(l_x\) 的点 \(y\),代价是 \(p_xdis(x,y)+q_x\),求每个点到 1 号点的最小代价。

思路:其实还好,就是相当于维护一个区间李超树,可以根据出栈序,建立线段树套李超树就可以了。

P6775 [NOI2020] 制作菜品

题意:有 \(n\) 种质量之和为 \(m\times k\) 的原材料,你需要构造 \(m\) 道菜,使得每道菜使用最多两种原材料整数克,且使用恰好 \(k\) 克原材料。

思路:考虑为什么有部分分\(n-2\leqslant m\)。先考虑如果\(m=n-2\),把\(d\)从小到大排序,那么显然有\(d_1<k,d1+d——n\geqslant k\),因此把\(d_1,d_n\)一起做一道菜,这样就变成了一个更小的子问题,可以递归处理。

如果\(m>n-1\),那么可以向\(m=n-1\)转化,每次直接用\(d_n\)做一道菜,就可以转成\(m=n-1\)的问题,这样就解决了。

再考虑\(m=n-2\),这时有结论,问题有解当且仅当可以找到一个子集\(S\)满足\(\sum\limits_{i\in S}d_i=(|S|-1)k\),充分性显然,必要性可以考虑建图,因为\(m=n-2\),那么一定不联通,而要满足条件就必须存在一个满足条件的子集。

于是直接用bitset优化背包就行了。

P9479 [NOI2023] 桂花树

思路:发现题目的条件等价与\(\forall 1\leqslant i\leqslant n+m,[1,i]\)的虚树中节点不超过\(i+k\)。因为我们只需要考虑每个未确定的节点不能超过多少,因此我们可以用一个\(k\)位二进制数来表示。考虑每加入一个新节点\(i+1\),有4种方式,接在一个点的后面,加在一条边上,作为一个未确定的节点,以及把一个在\((i+1,i+k+1]\)的节点加在一条边上,然后把\(i+1\)接在后面。直接状压DP就可以做到\(dp(i,S)\)

P4027 [NOI2007] 货币兑换

题意:比较简单的李超树优化DP题。

\(f_i\)表示第\(i\)天能获得的最多钱数,\(A_i\)表示第\(i\)天能获得的最多\(a\)券数,\(B_i\)同理,那么有

\[f_i=\max\{f_{i-1},\max\limits_{j=1}^{i-1}a_iA_j+b_iB_j\} \]

变形后就是\(f_i=b_i\max(A_j\frac{a_i}{b_i}+B_j)\)

于是可以直接李超树。

P5472 [NOI2019] 斗主地

思路:猜到答案是一次函数或者二次函数,于是求出函数表达式后 \(O(1)\) 计算即可。

P7737 [NOI2021] 庆典

题意:给定一张有向图,满足特殊性质:对于三座城市 \(x,y,z\),若 \(x\Rightarrow z\)\(y\Rightarrow z\),那么有 \(x\Rightarrow y\) 或者 \(y\Rightarrow x\),多次询问,每次临时加上 \(k\) 条有向边(询问之间互相独立),问从 \(s_i\) 出发到达 \(t_i\) 可能被经过的点数,一个点可以被重复经过。

思路:好题。

首先,考虑原题给的条件有什么用。我们缩点后,发现每个点只保留最后一条与之相连的入边是不影响可达性的,因此再进行拓扑排序后就会形成一颗叶向树。

对于询问,可以分类讨论也可以建虚树。我们把 \(s,t\) 及新边的端点拿出来,点权就是原树上的点权,祖先、后代之间连边权为树上不包含端点的点权和的边,新建的点之间连 0 的边,然后问题就转成了求 \(s\)\(t\) 可能经过的点权和,可以暴力地求出 \(s\) 能到的边和能到 \(t\) 的边,求交即可。

P7739 [NOI2021] 密码箱

思路:算是神秘平衡树维护矩阵题。

首先,因为有 \(x+\frac{z}{y}=\frac{xy+z}{y}\),而 \(\gcd(xy+z,y)=\gcd(z,y)\) ,因此我们不用考虑约分,可以直接把分子分母分开算。

我们用向量 \((x,y)\) 表示分数 \(\frac{x}{y}\),因为 \(f(a_i,\frac{x}{y})=\frac{a_iy+x}{y}\),所以我们可以把这个变换写成 \((x,y)\) 右乘 \(\begin{bmatrix}a_0&1\\1\end{bmatrix}\),题目要求的就是从右乘到左的答案。

接下来考虑操作。

对于 \(w\) 操作,因为 \(\begin{bmatrix}1&1\\&1\end{bmatrix}\times\begin{bmatrix}a_n&1\\1\end{bmatrix}=\begin{bmatrix}a_n+1&1\\1\end{bmatrix}\),于是可以在后面加 \(\begin{bmatrix}1&1\\&1\end{bmatrix}\)

对于 \(e\) 操作,发现答案的变化都是 \(\dfrac{x}{y}\rightarrow \dfrac{(a+2)x+(a+1)y}{x+y}\),于是就相当于是加了矩阵 \(\begin{bmatrix}0&-1\\1&2\end{bmatrix}\)

然后就可以用 fhq 维护了。需要维护 6 个信息,区间乘积,取反后乘积,翻转后乘积,取反并翻转后乘积。

P7740 [NOI2021] 机器人游戏

思路:NOI 的神仙题。

大概理清了思路。

首先,样例解释告诉我们,可以用容斥来计算答案。于是我们枚举那些位置有起点,然后对于每个起点,我们可以求出这个点为起点对其他位置的限制,每个位置最终的限制是 0/1 , \(x/1-x\),考虑每个位置的贡献:

  1. 如果机器人爆炸了,那么可以当做贡献是 1。

  2. 如果同时拥有前两个或后两个,那么说明只能为空格,贡献是 1。

  3. 如果只有一种,那么每种输入都有唯一对应的输出,贡献为 3。

  4. 如果前两个里面有一个,后两个里面有一个,那么只能填 0/1 ,贡献为 2。

于是我们就可以做到 \(O(n^2m2^n)\)

考虑怎么优化。我们发现 32 这个数字很可以折半,那么就往这方面入手。我们发现,如果最后的起点在位置 \(p\),那么不让机器人爆炸的话 \(R\) 最多只有 \(n-p\) 个,于是考虑枚举最后一个起点的位置 \(p\)

  1. \(p\leqslant\dfrac{n}{2}\),那么直接枚举就是 \(O(2^{\frac{n}{2}})\)

  2. \(p>\dfrac{n}{2}\) ,那么 \(p\) 只会影响 \([p,p+\frac{n}{2}]\) 这些位置,这些位置之外的位置只有起点个数是否大于 0 有影响。即对于每个位置,只有前 \(\dfrac{n}{2}\) 个位置和在这之前是否有起点会造成影响。

于是就可以 DP 了。设 \(f[i][S][0/1]\) 表示当前考虑到点 \(i\) ,前 \(\dfrac{n}{2}\) 个位置的状态为 \(S\) ,前 \(\dfrac{n}{2}\) 个位置前有没有起点,然后就可以做到 \(O(nm2^{\frac{n}{2}})\)

发现这样还是不可过,但是我们发现每一种限制都可以用 0/1 表示,很自然地就可以想到用 bitset 优化,即维护 4 个 bitset 分别表示 4 种限制是否存在,这样复杂度就是 \(O(\dfrac{nm2^{\frac{n}{2}}}{w})\)

P8499 [NOI2022] 挑战 NPC Ⅱ

题意:有两棵树,要求是否可以把第一棵树删掉一些节点后和第二棵树同构。

思路:想的差不多是对的。

做法十分暴力:我们先求出每个子树的哈希值,然后对于两棵树,设 \(f(x,y)\) 表示第一棵树以 \(x\) 为根节点的子树能否在删掉若干点后与第二棵树以 \(y\) 为根节点的子树同构,这时我们发现,如果有两个子树同构,我们可以直接将他们匹配,最后剩下的不会超过 \(k\) 个子树,我们可以枚举全排列来匹配,然后递归下去就可以了。复杂度看似是 \(O(nk!k)\) 的,但实际上是 \(O(n2^k)\) 的。

一些小剪枝:第一棵树中的节点子树个数不能小于第二棵树,以及如果子树大小相等那么可以直接判哈希值来看是否合法,不用再递归下去。

P8500 [NOI2022] 冒泡排序

题意:求出一个序列,有 \(m\) 个限制形如 \([l_i,r_i]\) 的最大值为 \(w_i\),要求最小化逆序对数。

思路:神仙题。

先从subtask入手。

对于subtask1,只有0/1,那么先把全1的段填好,考虑剩下的位置。对于每个0的限制,我们把必须要填的0越往前填越好,所以可以把这些0填上,剩下的位置就是可以随意填的了。同时有一个性质,对于任意填的部分,他们之间是不会产生逆序对的,因此一定是一段前缀填0,后缀填1,直接枚举交界点就可以算贡献。

对于subtask2,也可以通过之前的结论得出,每个可以随意填的位置的贡献就是当前最优解,而全局最优解就是由这些当前最优解得出,于是可以简单地每个位置都取局部最优解。

对于subtask3,和subtask2唯一的区别就是一些点有下界的限制,这时只需把局部最优解变成找线段树上\(\geqslant k_i\)的位置上的最小值就行了。

正解:其实就是把subtask3拓展一下,对于相交的区间,如果\(k\)不同,那么可以缩小一个区间,否则就可以转成A性质,然后用同样的做法就可以了。

P9480 [NOI2023] 深搜

题意:终于来写深搜了。

超级神仙题。

首先,合法的条件等价于所有非树边都是返祖边。于是就有了 36 分做法,即枚举关键点的选取情况 \(S\),求出 \(c(S)\) 表示满足条件的非树边数量,那么答案就是 \(\sum\limits_{S}(-1)^{|S|-1}2^{c(S)}\)。同时我们有了初步做法:对 \(S\) 进行容斥。

我们建出 \(S\) 的虚树,称一个点 属于 虚树当且仅当它是虚树的节点,称一个点 落在 虚树上当且仅当这个点被虚树的一条边覆盖,而且这时如果虚树的根为被选中且只有两棵子树在虚树上,那么这个点不属于虚树。

于是对于一条非树边,它合法有两种情况:

1.落在虚树上的向虚树外延伸的某条边的对应子树内的返祖边(以该点为根)。

2.被虚树的某条边完全包含。

性质 B

先从性质 B 入手。

这时因为没有横叉边,我们可以把这里的虚树当成正常的虚树,对答案没有影响。考虑在虚树的根 \(x\) 处统计答案,因为不存在横叉边,因此子树的贡献是独立的,可以单独计算。设 \(out_x\) 表示以 \(x\) 子树外以 \(x\) 为根的返祖边数量,\(in_x\) 表示 \(x\) 子树内以 \(x\) 为根的返祖边数量,可以换根求出。子树外的贡献就是 \(2^{out_x}\)

接着就是子树内的贡献。设 \(f_x\) 表示当 \(x\) 属于虚树时子树内的贡献。对于 \(x\) 的一个孩子 \(y\),即一个子树里最浅的点,那么贡献就是 \(\prod\limits_{y\in Y}f_y\),这是点的贡献。

接着是非树边的贡献。分 3 种:

  1. 对于每个 \(y\in Y\) 包含在 \(y\)\(x\) 的路径上的非树边。
  2. 对于每个在 \(y\)\(x\) 的路径(不含端点)上的点,它向虚树外延伸的某条边对应子树内的返祖边。
  3. \(x\) 向虚树外的返祖边。

对于第三类非树边,我们可以摊到每个没有被选的孩子处计算。

  1. 对于选了点的孩子 \(y\),设 \(g_{y\rightarrow x}\) 表示对于 \(x,y\) 前两类非树边的数量,贡献是 \(X(y)=\sum\limits_{z\in\text{subtree}(y)}g_{z\rightarrow x}\)
  2. 对于未选点的孩子 \(y\),设 \(c\) 表示 \(y\) 子树中返祖边的数量 \(in_y\) 加上一段为 \(d\),一段在 \(y\) 子树内的非树边的数量,贡献系数是 \(Y(y)=2^c\)

那么就可以计算 \(f(x)\) 了。假设在 \(k\) 棵子树里选点的答案为 \(F_k=\prod(X(y)x+Y(y))\),有两种情况:

  1. \(x\) 不是关键点,那么要至少两棵子树被选,即为 \(\sum\limits_{k\ge 2}F_k\)
  2. \(x\) 是关键点,贡献是 \(-\sum F_k\)(因为容斥)。

因为 \(\ge 2\)\(k\) 都是等价的,于是可以用背包维护 \(F_0,F_1,F_{k\ge 2}\),这样答案就是 \(-\sum f_i2^{out_i}\)

接下来就是怎么通过 \(g_{z\rightarrow x}\)\(X(y)\)

  1. \(g_{z\rightarrow x}\leftarrow g_{y\rightarrow x}\)\(y\)\(x\)\(z\) 方向的儿子。
  2. \(g_{y\rightarrow x}\leftarrow f_y\)
  3. 第一类非树边,考虑每条边 \((y,x)\) ,对于 \(\forall z\in\text{subtree(y)}\)\(g_{z\rightarrow x}\) 乘以 2。
  4. 第二类非树边,对于 \((y,u)\) 满足 \(u\)\(y\) 的子树里,设 \(u\) 对应的 \(y\) 的孩子是 \(v\),那么所有 \(y\) 的所有不是 \(v\) 的子树里 \(g_{z\rightarrow x}\) 乘以 2。

实现上,先继承 \(g\),然后加入第一类非树边的贡献,算 \(X\)\(f\),加入第二类非树边的贡献。把 \(z\) 这一维放到 dfs 序上,可以直接用线段树维护。

对于 \(Y(x)\),我们在加入第一类非树边的贡献时,对于 \((x,z)\),把 \(x\)\(z\) 方向的儿子 \(y\)\(in_y\) 加 1 即可。 \(Y(x)=2^{in_x}\)

正解

考虑会多出什么情况。如果虚树的根 \(x\) 本身不属于虚树,而且存在经过 \(x\) 的虚树边 \((u,v)\),那么一条两端属于 \(u,v\) 的树上路径且两端分别在 \(x\) 两侧的非树边就不合法了。简单来说就是 \(x\notin S\),但 \(x\) 有恰好两棵子树中有属于 \(S\) 的点。

于是有一个 \(O(n^2m)\) 的做法:我们枚举 \(x\) 虚树上的两个孩子 \(a,b\) 和对应的 \(x\) 的儿子 \(A,B\),如果存在一条非树边 \((u,v)\)\(u,v\) 分别在 \(a,b\)\(x\) 的路径上(不含端点),那么贡献会乘 2。

我们可以先对于每个孩子 \(y\),把所有 \(g\) 值乘上 \(\dfrac{1}{Y(y)}\),最后全局乘上 \(\prod(Y(y))\),这样每个 \(a,b\) 的贡献就与其他子树无关。

我们发现,一条上述边的贡献相当于是把一个矩形乘上 2,然后求全局和,这一步可以考虑扫描线。最终要减去之前算的 \(F_2\),于是还需维护 \(F_{k\ge 3}\)

复杂度 \(O((n+m)\log n)\)

P9483 [NOI2023] 合并书本

题意:给定长度为 \(n\) 的正整数序列 \(a\),以及一个初始全部元素为 0 的序列 \(b\)。每次操作可以将 \(a\) 中选取两数 \(a_i,a_j\) 合并到 \(a_j\) 上,代价为 \(a_i+b_i+b_j\) ,并使 \(b_j\leftarrow 2\max(b_i,b_j)+1\)。求将所有数合并为一个数的最小代价。

思路:NOI 的神仙题。

考场上只写 10 分的暴力,甚至连状压和区间 DP 的都没写出来。

对于一棵树,它的磨损值之和为所有非叶节点的磨损值减去根节点的磨损值,但是我们不能直接得到每个非叶节点对重量的贡献,于是就考虑把贡献 下放 到叶节点进行统计。每个叶子的 重量贡献系数 就是它与其他子树合并的次数,而如果求出了每个叶子的重量贡献系数,那么统计答案可以排序后贪心选取,于是问题就是然后求出重量贡献系数。

因为是树形结构,于是我们可以考虑自底向上和自顶向下两种方法。

前者好像最多可以做到 \(n\leqslant 50\) ,后者可以做到 \(n\leqslant 100\)

自顶向下就是从根节点开始,每次分裂叶子得到新的叶子。我们记当前重量贡献系数的集合为 \(S\) 和当前整棵树的磨损值之和。如果一个子树的最大深度是 \(d\),那么就当成在第 \(d\) 层,这样每一次分裂非叶节点的层数都会加 1,于是所有的非叶非根节点的权值都会 \(\times 2+1\) ,于是磨损值会变成原来的两倍。

再考虑 \(S\) 的变化。原来的一个贡献系数为 \(c\) 的叶子会分裂成系数为 \(c,c+1\) 的两个叶子,那么假设要分裂的集合是 \(T\) ,那么 \(S\) 会变成 \(S\)\(T\) 加上1 后的并,因此我们每次肯定是选择最小的 \(|T|\) 个叶子进行分裂。

就这样可以做到 \(n\leqslant 70\)

我们发现,根据之前的每一次分裂非叶节点层数都会加 1,这意味着前一轮分出的两个叶子至少有一个要在这一层分裂,因此 \(|T|\) 单调不降。同时,我们其实并不需要记录 \(T\) ,因为由 \((v,m)\)\((v',m')\) 合并到 \((\min(v,v'),\min(m,m'))\) 一定不劣。这样总状态数就很少了。

P6776 [NOI2020] 超现实树

思路:终于来写超现实树了。

首先,发现只有 “链树” 有用。链树即每个点的左右儿子大小的 min 不超过 1 的树。

证明可以考虑把一棵不是链树的树的一些子树砍掉,这样可以生成更多的树。

其次可以发现如果不能产生无数链树,那么就可以证明是近乎完备的。

因此我们需要判断这些链树是否可以生成无数链树。

我们考虑怎么刻画一棵链树。对于链树的每个节点,有 4 种状态:

  1. 有左子树,挂了叶子
  2. 有右子树,挂了叶子
  3. 有左子树,无叶子
  4. 有右子树,无叶子

于是我们把四叉树建出来,然后把每棵链树的底端打上标记,最后如果可以不经过标记节点走出这棵树那么就不合法,否则就合法。

P8501 [NOI2022] 二次整数规划问题

题意:求一个序列 \(x\),要求满足以下条件:

  1. 每个位置有限制 \([l_i,r_i]\),其中 \(1\le l_i\le r_i\le k\)
  2. 有若干个限制,表示 \(|x_i-x_j|\le d\)

同时给出长为 \(k-2\) 的序列 \(v_2\cdots c_{k-1}\),记 \(G\) 为满足 \(|x_i-x_j|\le 1\) 的二元组 \((i,j)\) 个数,记 \(c_i\) 表示 \(x_j=i\) 的数量,那么权值定义为 \(10^6G+\sum c_iv_i\),求满足条件的序列的权值最大值。

思路:绝世好题。

就从部分分开始吧。

k=3

发现一个性质,就是如果一个元素的取值不固定,那么一定取 2 最优。

证明比较简单,因为把一个元素变成 2 不会影响第二类限制是否满足,而且这样不会让 \(c_2\) 和 G 减小,那么就不会更劣。

于是就可以在 \(O(n+m+q)\) 的时间里解决 \(k=3\)

k=4

考虑能否延用 \(k=3\) 的做法。

这时有一个结论,对于任意的 \(k\) 和非负 \(k-2\) 元组 \((c_2,c_3,\cdots,c_{k-1})\),每个非必须取 \(1\)\(k\) 的元素取 \([2,k-1]\) 一定不劣。

证明和上一个证明类似。

于是这时就只需判断每个元素是取 2 还是取 3。这时它们之间对 G 的贡献是一定的,于是每个数取 2 和取 3 对答案的贡献是线性的,于是一定是全取 2 或者全取 3 更优。

不过在确定每个元素的取值范围是要注意必须取 1 和 \(k\) 的元素对其他元素的影响,可以用差分约束来求出每个元素最终的取值范围。

特殊性质 A

因为选的数之间没有限制,因此需要考虑的取值范围只有 \([2,3],[2,4],[3,4]\)

此时有结论,对于取值范围相同的元素,它们都取同一个数是最优的。于是只有 \(2\times2\times 3=12\) 种情况,直接计算即可。

复杂度 \(O(n+m+q)\);

特殊性质 B

这时可以考虑暴力枚举每个被限制的元素的取值,考虑在有限制的变量之间连边,那么对于每个连通块,\(c_2+c_3+c_4\) 是定值,于是可以用一个二元组 \((c_2,c_4)\) 来描述,可以在 \(O(3^mm)\) 的时间内求出所有合法的 \(O(m^2)\) 个二元组。没有限制的元素可以像特殊性质 A 一样处理。

复杂度 \(O(n+3^mm+qm^2)\)

\(\sum q\) 较小

我们可以把对 \(G\) 的贡献看成每一对二元组之间一个选 2 一个选 4 会有 \(10^6\) 的贡献,不满足限制的代价是 \(\infty\),就相当于是两个元素差值大于 \(b_j\) 就产生一定代价,发现这很像 [HNOI2013] 切糕 的建模,于是可以直接用最小割。

特殊性质 C

通过上一个做法,我们不难想到可以求出所有可能作为最优解的二元组 \((c_2,c_4)\),于是可以用随机化。我们随机 \(v_2,v_3,v_4\) 的取值,然后计算出最优的 \((c_2,c_4)\),大概随 500 次左右就没问题。

对于 \(100\%\) 的数据

从特殊性质 C 我们不难发现,我们只关心哪些二元组 \((c_2,c_4)\) 能取到最优解,其他的都不重要。

我们先把答案写成只有 \(c_2,c_4\) 的样子。为了避免出现 \(n\),我们不直接求答案,而是求 \((c_2,c_4)\) 相对于 \((0,0)\) 增量:\(c_2(v_2-v_3+2\times 10^6c_1)+c_4(v_4-v_3+2\times 10^6c_5)-2\times 10^6c_2c_4\),可以写成 \(-2\times 10^6(c_2-C_1)(c_4-C_2)+C_3\),于是就要最小化 \((c_2-C_1)(c_4-C_2)\)

我们把二元组 \((c_2,c_4)\) 放到平面上,设 \(\min c_2\)\(c_2\) 的最小值,那么我们一定可以取到 \((\min c_2,\min c_4),(\min c_2,\max c_4),(\max c_2,\min c_4)\), 同时所有点都在 \(([\min c_2,\max c_2],[\min c_4,\max c_4])\) 内,这样求 \((c_2-C_1)(c_4-C_2)\) 的最小值就相当于是把所有 \((c_2,c_4)\) 平移后取横纵坐标乘积的最小值。

这时已经确定了的有左下、左上、右下 3 个点,于是只要矩形最后不是全部落在第三象限,取这 3 个点都最优,于是只用考虑矩形全部在第三象限的情况。

因为是坐标乘积最小,不难想到凸包,那么我们就需要求出凸包上的所有点,于是可以用 [BalkanOI2011] timeismoney | 最小乘积生成树 的做法——分治求凸包上的点。具体地,假设当前要找到在 \((x_1,y_1),(x_2,y_2)\) 间的凸包,那么就是要找到一个点 \((x,y)\) 满足 \((x_1-x_2,y_1-y_2)\)\((x,y)\) 的叉积最大,如果 \((x,y)\) 不在线段 \((x_1,y_1,x_2,y_2)\) 上,那么其就是一个新的在凸包上的点,可以向两边递归下去。

那么问题就是每个元素取 2 有 \(y_1-y_2\) 的贡献,取 4 有 \(x_2-x_1\) 的贡献,要让总贡献最大。我们可以先都加上 \(y_1-y_2+x_2-x_1\),于是取 2 有 \(x_2-x_1\) 的代价,取 4 有 \(y_1-y_2\) 的代价,取 3 有 \(y_1-y_2+x_2-x_1\),要求最小代价。这时有影响的限制就是 \(|x_{p_j}-x_{q_j}|\le1\),即这两个元素不能一个选 2 一个选 4。

于是考虑建立最小割模型。我们把每个元素拆成 2 个点 \(p1_i,p2_i\),然后钦定割掉 \(S\rightarrow p1_i,p1_i\rightarrow p2_i,p2_i\rightarrow T\) 分别代表取 2、3、4,那么每个限制就可以连边 \(p2_i\overset{\infty}\rightarrow p1_j,p1_i\overset{\infty}\rightarrow p2_j\)。这样我们跑一遍最小割就可以求出最优的 \((c_2,c_4)\)

P2178 [NOI2015] 品酒大会

题意:对于每个长度,求有多少对后缀的 lcp 不少于这个长度和后缀权值乘积的最大值。

思路:从后缀的 lcp 入手不难想到后缀数组,而求出 \(height\) 数组后两个后缀的 lcp 就是 \(height\) 数组的区间最小值,那么我们要求的就是有多少个区间的最小值是某个数。

考虑按 \(height\) 从大到小加入点,那么每一次的贡献就是两边的连续段长度的乘积,同时维护最大最小的权值,合并可以类似并查集,这样复杂度就对了。

P1973 [NOI2011] NOI 嘉年华

题意:给出一些区间,让你在这些区间中选出两组,两组区间不能有交。求:
① 让两份中分到区间数最小的一份,尽量得到更多的区间。
② 在第 \(i\) 个区间必须选的情况下的上一问答案。

思路:首先,区间可以离散化,于是预处理出来在 \([l,r]\) 内的区间有多少个。

对于第一问,可以设 \(pre[i][j]\) 表示前 \(i\) 个位置,选择了 \(j\) 个区间,另一组的区间数量的最大值,转移时直接枚举另一组的位置的端点 \(k\) 来转移。设 \(suf[i][j]\) 表示第 \(i\) 个位置往后的答案,求法类似。

那么第一问的答案就是 \(\min(pre[m][j],j)\) 的最大值。

然后考虑第二问。初步的想法就是钦定 \([l_i,r_i]\) 必须选一组,然后枚举两边选了多少,设 \(f[l][r]\) 表示钦定选 \([l,r]\) 的答案,那么有:

\[f[l][r]=\max\limits_{x,y}\min(x+cnt[l][r]+y,pre[l][x]+suf[r][y]) \]

这样一个区间的答案就是 \(\max\limits_{[l,r]\in[x,y]}f[x][y]\)

现在问题是求 \(f[l][r]\)\(O(n^4)\) 的,考虑怎么优化。对于上面的式子,我们发现当 \(y\) 的决策关于 \(x\) 的决策有单调性,即当 \(x\) 增大时,最优的 \(y\) 不会增大,于是把 \(y\) 从大往小扫即可。

P1963 [NOI2009] 变换序列

题意:给定 \(i,a_i\) 的距离,求字典序最小的 \(a_i\)

思路:可以把 \(i\) 和可行的 \(a_i\) 连边,那么我们要求的就是字典序最小的完美匹配,可以直接用匈牙利算法来解决。

在此题中,每个点左部点最多连两条边,所以直接倒着匹配就是字典序最小的方案。

P1912 [NOI2009] 诗人小G

题意:把序列划分成若干段,每一段的代价是 \((\sum a_i-r-l-x)^p\),求最小代价。

思路:\(O(n^2)\) 的 DP 是好想的,考虑优化。

这个 \(p\) 次方很容易让人想到决策单调性,而且确实可以证明,可以根据导数是递增的来证。

于是可以用二分栈的经典做法来实现,复杂度 \(O(n\log n)\)

P2805 [NOI2009] 植物大战僵尸

题意:给定一个 \(n\)\(m\) 列的图,每个点可以保护一些点,每个点有一个权值,如果要选择一个点的权值必须选择保护它的点和它右边的点,求最大权值。

思路:容易发现这就是最大权闭合子图的模型。最大权闭合子图是每个点有权值,如果要选择一个点就要选择这个点的所有出边,要求最大权值。

做法是对于每个点,如果权值非负,就由源点向这个点连边,否则就向汇点连边,边权就是点权的绝对值,然后对于原图中的边 \((u,v)\),连 \(u\leftarrow v\) 的边,边权是 \(inf\),然后跑最小割,答案就是正点权和减去最小割。

为什么这样做是对的呢?首先,能被割掉的边一定是连向原点或者汇点的边,那么考虑与 \(S\) 联通的集合 \(X\) 和与 \(T\) 联通的集合 \(Y\),我们选择的点就是在 \(X\) 中的点。对于一个正权点,如果划分到了 \(Y\) 集合,就要付出 \(val\) 的代价,而不选择相当于是去掉原来加上的代价;对于一个负权点,如果划分到了 \(X\) 集合,那么代价是 \(|val|\),相当于是加上原先负的代价。同时我们选择的点集 \(X,Y\) 中也不会有连边,相当于是选择了 \(X\) 中点的导出子图,于是这样做是对的。

回到这题,我们发现几乎是一样的,但是问题在于可能会出现环,于是可以建反图跑拓扑排序来去掉环的影响。

P7735 [NOI2021] 轻重边

题意:给一棵树,每条边可能是轻边或者是重边,有两种操作,一是把与一条路径上的点相连的路径变为轻边,然后把路径上的边变成重边,二是查询一条路径上有多少条重边。

思路:直接做很难,考虑怎么转化成更好做的形式。我们考虑把第一种操作变成给点染色,每一次操作就染成新的颜色,那么一条边两端颜色相同就是重边,否则就是轻边。

这个就可以直接用树剖维护了,相当于是在线段树上维护相同颜色连续段数及长度。不过在查询时合并处细节较多。

复杂度 \(O(m\log^2n)\)

posted @ 2024-02-14 11:45  Xttttr  阅读(36)  评论(0编辑  收藏  举报