因为一些奇怪的理由也来做集训队作业了。
基本上完成了任务,可能不会再更新了 /kk
[NEERC2013]Dictionary
Description
给一些单词,要求建一棵节点最少的字典树,使字典序中包含给定所有的单词(单词路径在树上出现过)。
Solution
若 (a) 是 (b) 的子串,那么 (b) 的限制强于 (a),(a) 可以删去。
接下来我们就得到了两两不包含的单词集。
考虑如果要把单词 (x) 插入到当前字典序,可以找一个包含 (x) 最长前缀的位置,这样可以添加尽量少的后缀凑满整个单词,并且这步并不影响后续单词加入,因为字典树能表示的串还是一样,因此是独立的。并且 (x) 最长前缀一定仅属于一个字符串,否则即跨越了两个字符串,即 (x) 包含那个字符串,这种情况已经去掉了。
因此对于两个单词 (a, b),可以设 (w(a, b)) 为把 (b) 接在 (a) 后面的最小花费,即 $len_b - $ (b) 的前缀与 (a) 能匹配的最长长度,这个东西可以 KMP 快速求,不过此题没有必要。
把这个东西看成一个有向图,这样构成一个联通的字典树 (Leftrightarrow) 该图联通。
枚举根,求最小树形图即可。
这题还要记录方案,考虑朱刘算法的定义,缩点的新边选择的意义是选择这条边,去掉原来的入边。每一层每种新边的选择对应选1杀1,因此每次新边可以新开一个编号,在最终状态,考虑每个选择编号对应上一层选择不选哪两条边即可。把边弄出来之后,需要构造字典树,这里注意每个点可能代表若干串的某个下标,而边的含义是某点要在某点的某处往伸,我是把边预处理成最靠近根的点的位置。
时间复杂度 (O(n^4))。
[CERC2015]Export Estimate
Description
给你一个无重边、自环的无向图,有 (q) 次询问,每次询问一个阙值 (t),问你把图中 (< t) 的边删掉以后,从小到大编号考虑每个点,如果孤立点就删掉,如果度数为 (2) 就把它变成一条边(不能是自环) ,问最终状态的点边数,点边询问数都是 (le 3 imes 10^5) 的。
Solution
发现两种操作均不影响每个点的度数,第二个操作可以看作把一个点连向两个点转化成了一个边。
而点边的删除的条件都是点的度数,因此考虑点的顺序并不影响图的结构,只会影响删除/留下点的编号,但这个东西对我们没用。
那么我们就可以用一个经典的思路,把边从大到小排序,依此插入边,考虑当前的图对应的点边结构是多少,然后加入那些处于这个状态的答案。我们考虑何种情况的一个点会被删除:
- 度数 (= 0) 的点。
- 度数 (= 2) 的点且最终状态并不是一个自环。并不是一个自环的条件只能在纯粹的环(即所处联通块只有一个简单环,没有其他冗余的东西)中取到,想象一下它会以此把编号最小的点换成一条边,最终会留下一个编号最大的点的自环。而其他情况,不可能出现度数为 (2) 的点连的是自环的情况,如果不构成一个环,那么原来图里没自环,连接的两个方向点必不可能缩成同一个点(大小为 (2) 的纯粹环)。如果构成一个环,但环上有其他杂边,那么不会缩成只有一个点,因为必然有一个点度数 (> 2)。
因此最终状态点的数量 $= $ $n - $ 度数为 (0) 的点数 (-) 度数为 (2) 的点数 $+ $ 纯粹的环数
然后再考虑何种情况边的数量会改变:
- 度数 (= 2) 的点且最终状态并不是一个自环 的点,会让两条边数变成一条边,道理同上。
因此最终状态点的数量 $= $ $m - $ 度数为 (2) 的点数 $+ $ 纯粹的环数。
其他东西维护都比较简单,纯粹的环数等价于该联通块每个点度数 (= 2)。用一个并查集维护联通块中度数为 (2) 的点数就好了。
时间复杂度可以做到线性(不排序,用桶,并查集两个优化做到常数)。
[NEERC2016]Delight for a Cat
终于 TM 不看题解做出来一道(瞄了一眼标签)
Description
你要构造一个长度为 (n) 的 (01) 序列,每个位置选 (0/1) 的权值给定。
要求在任意 (k) 连续段的 (0) 的数量大于 (m_s),(1) 的数量大于 (m_e),在这种状态下最大的总权值。
还要输出一种方案。
Solution
这题和 NOI2008 志愿者招募 挺像的。
看起来很像 DP,但 DP 要记录每个点选的啥才能保证满足限制,因此不能用 DP。
发现要满足的限制是一个带变量的不等式,我们需要在限制内,选择一种变量的方案,使答案最大化。
可以考虑设置一些变量,变不等式为等式,利用网络流的流量守恒,使每个点等于一个等式,我们只需要将等式变换让每个变量最多出现在一个等式的左侧和另一个等式的右侧(或者只出现一次):
- 若出现两次,让右侧的等式点指向左侧的等式点连一条边。
- 若出现一次,若右侧则让这个等式点指向网络流虚拟汇点连一条边。
对于等式中的常量,左右侧连和源汇点连边,考虑到费用流首先保证最大流,因此一定会让它充满。
这条边的流量就映射着这个变量的选择,因此变量的形式应该是 (0 le x le a)。((x) 为变量,(a) 为常量),这样可以保证每个变量在所有等式中是同一个值。
因为睡觉和吃是补集关系,我们不妨只考虑在那些位置吃饭。设最初我们都选择睡觉,然后考虑每个点的权值为 (e_i - s_i),即从睡觉到吃变换的权值。
设 (x_i in { 1, 0}) 为第 (i) 天是否选择吃饭,那么需要满足对于任意 (K le i le n), (m_e le displaystyle sum_{j=i-k+1}^i x_j le k - m_s)。
化不等式为等式,设 (t_i) 为这个等式的一个阙值,满足 (0 le t_i le k - m_s - m_e),这样上面那个式子就等价变成了:
但是呢现在每个变量出现在了多个等式中,考虑把等式进行等价变换,保留 (i = k) 的等式,(i > k) 的等式做一个差分,等价变成 (i) 减去 (i - 1) 的等式,如此,我们得到了 (n - k + 1) 个等式:
- (i = k):(displaystyle sum_{j=i-k+1}^i x_j + t_i = k - m_s)
- (i > k):(t_i + x_i = t_{i-1} + x_{i-k})
然后我们发现变量就最多出现在两个式子中了,因此跑最大费用最大流就好了。
还要输出方案,根据实际意义在网络流里看对应边有没有流满就好了。
网络点边数都是 (n) 级别的。由于网络流不容易卡满,因此可以跑过。
[NEERC2016]Binary Code
终于不看题解做出来第二道(瞄了一眼标签),一开始还口胡错了。。
Description
给你 (n) 个 01 串,每个串至多有一个 ( ext{?}) 你可以选择让他变成 (0) 或 (1),求是否存在一组方案使得两两串都不互为前缀, (n, sum |S| le 5 imes 10^5)。
如果存在还要输出一种方案。
Solution
每个串只有两种状态,即一个 2-SAT 问题。
暴力的考虑,任意两个不符合的状态都要连两条有向边,这样是 (O(n^2)) 的。
考虑前缀的关系是一个线性包含关系,如果我们把这些状态串放到 01Trie 上,那么每一条链上的点最多选一个,即选一个点的影响是它的子树与祖先上的状态都不能选。
我们可以再创两个虚点,一个叫下传点,一个叫上传点,两个点都指向自己的另外一个状态。下传点向所有儿子连边,上传点向父亲连边。(这里的儿子父亲都是指在 Trie 上同一条链上,当前点的状态串对应的前驱后继状态串编号,并非 Trie 的点,如果是本质相同的串,先后顺序没有影响,都是选一个会把其他给否掉)。
这样选择一个编号我们就能以他的度数量级的有向边表示他的限制关系里。
度数和虚点都是和 (n) 同阶的。
因此复杂度 (O(n + sum |S|))。
[CERC2014]The Imp
这种每次要从里面选一个物品买,方案是一个排列的,(n) 非常大,通常是证明最优决策在一条链上,然后排个序背包。
假设证明的了最优的决策在一个线性顺序上,即按 (1 sim n) 的顺序考虑是否买物品,那么可以列出如下 DP:
- 设 (f_{i, j}) 考虑完 (i sim n) 的物品,用了 (j) 次咒语的最大收益:
- (f_{i, j} = max(f_{i+1, j}, min(f_{i+1,j+1}-c_i, v_i - c_i)))。外面的最大化表示是你的自由选择,里面的最小化是小鬼的自由选择。
考虑物品的排列假设是 (p),设 (x = p_i, y = p_{i+1}),我们考虑何时交换不优。
考虑把这两个物品的贡献打包起来,转移应该是形如 (f_{i,j} = max(f_{i+2,j}, f_{i+2,j+1} + k, f_{i+2,j+2} - c_x - c_y)),其中 (k) 是个常量,打包后如果是这期间用了零或二次咒语,顺序没有影响,因此只有用了一次又影响。
交换前,这个 (k = min(v_x - c_x, v_y - c_y - c_x))
交换后,(k = min(v_y - c_y, v_x - c_x - c_y))
即处理这个东西:
发现左边的第二项恒小于右边的第一项,右边第二项恒小于左边第一项。等式成立等价于 (v_x - c_x - c_y) 成为四者最小值,因此这两项不可能作为这个式子的最小值,上述式子就等价于:
因此按 (v) 排序做背包即可。
复杂度 (O(sum nk))
[ICPC2019 WF]First of Her Name
md 自己思维僵化想了好久。。结果这么模板的题还是需要瞄题解。
把那个名字看成一个字典树,把询问反转,就变成了这个询问字符串能作为字典树上有多少点的后缀。
考虑 AC 自动机的 fail 树,这个东西就是把一个串的后缀都放到了他的祖先链上。因此只需把 fail 树建出来,(n) 个位置的权值是 (1),求一遍子树权值和即可。
[CERC2017]Kitchen Knobs
考虑 (7) 是个质数,因此要么一个轮盘数字全一样就是怎么转就行,即没有限制,要么就是有唯一的旋转位置。把怎么转都行的剔除掉。
考虑把这个环的东西弄成模意义的数值问题,设位置状态是 (i),意义即顺时针转 (i) 次的状态,在 (mod 7) 意义下。那么问题就变成了一个序列,最初全为 (0),有一个目标状态 (a) 数组,每次可以连续选一段 (+k),问最少操作多少次变成目标状态。
考虑是操作是互逆的,可以将初始和目标反转,又考虑这个连续的一段操作,可以用差分变成两个点的操作,设 (b) 为 (a) 的差分数组,那么问题转化为:
- 一个操作可以选择一个 (l < r) 和 (k), 使得 (b[l]) 加 (k),(b[r]) 减 (k)。
问变成全 (0) 最少操作次数。
这个东西还是不太好做,考虑转化。发现一个关键性质就是每次操作不改变 (b) 的和。考虑一组最优解,把每次选择的 (l, r) 之间连边,那么对于每一个大小为 (cnt) 的连通图而言,需要满足他们的 (b) 的和模意义是 (0),并且至少操作 (cnt - 1) 次,如果我们能构造出一种 (cnt - 1) 次数的答案。因此就可以说明一定存在一组最优解是分成若干组,并且使用的是我们的这种构造方式,那么我们只需要求这种方式的最优值就好了。那么问题就等价于:
- 把序列分成若干组,每组的 (b) 和为 (0)。设有 (k) 组,每组大小是 (cnt_i),答案即 (sum_{i=1}^k (cnt_i - 1) = n - k)。
构造的证明:
考虑把这些点从左到右排序,依次是 (p_1, p_2, p_3,..., p_{cnt})。
考虑第 (i) 次操作,前 (i - 1) 个都变成 (0) 了,现在让 (b_{p_i}) 变成 (0)。
让 (l = p_i, r = p_{i+1}, k = 7 - b_{p_i}) 就好了。
因为和是 (0),因此前 (cnt - 1) 变成 (0) 了,最后一个也是 (0)。
因此我们需要最大化分的组数。
直接求是一个非常大的背包不太可做。
我们先考虑把 (a + b = 7) 这样大小可以为 (2) 的组给安排了。
那么最终 (1, 6) 、(2, 5),(3, 4),三种组,分配后剩下每组最多只有一种,因此剩下的数字种类最多 (3) 个。
用这三个(设为 (x, y, z))用的数量做一个三位背包就好了,考虑物品 (i imes x +j imes y+ k imes z),必须满足 (i, j, k < 7, gcd(i,j,k) = 1)(还有单独七个特判),否则可以继续分成两半,更优,可以打表发现物品个数最多是 (41) 个。
设剩下的数字种类每个数分别 (A_1, A_2, A_3)。
那么时间复杂度是 (O(prod A imes 41))。
用均值不等式发现这个东西最大是 (2e8) 左右,可以苟过。
[NWRRC2016] Java2016
对于 (0) 的部分样例给我们了。
剩下的情况我们只需要找到一种方式生成一个非常稳定的数 (a)。这样先除,再倍增,进制拼凑,能在很少的字符数内生成任意一个数。
生成的稳定数可以用 (max) 来凑,当 (2^{17}) 个 (max) 拼在不是 (255) 的概率是 ((frac{255}{256})^{2^{17}}) 这个东西可以估计为无穷小,对答案没有影响。
[NEERC2017]Laminar Family
第一道独立口胡出来的(没瞄标签。。虽然是个简单题。感觉和某一次做的 Div 3 最后一题很类似
考虑集合按所含点数大小从大到小排序,每次执行:
- 看这条路径上是否有不同颜色的点,若有则 (No)
- 否则把这条路径染成一种新的颜色
执行完毕就是 (Yes)。
正确性比较简单,考虑假设是 (No),找到相交且长度相邻的两条路径,在染一个的时候会发现它本身被比他更长的覆盖了,但没有被完全覆盖,因此必然相交。如果是 (Yes),绝对不会存在这种情况。
这样用树剖 + 线段树是 (O(m log ^2 n))
也可以考虑把上述操作逆序,即每次只需合并,查看当前路径所在联通块是否不包括其他点,考虑合并的时候每次都把指针移到当前联通块的最上端,这样每合并一次至少少一个联通块,故最多合并 (n - 1) 次,这样是 (O(m log m)) 的(排序。
[NWRRC2017] Consonant Fencity
简 单 题。
把问题转化成图论上的问题。
答案显然和序列顺序无关,考虑一个相邻的辅音字母点对 (a, b),在他们中间加一条边。
考虑把这个图每个点染成两种颜色之一,那么他们的那个权值就是不同颜色之间点的边权和。
那么我们反着考虑一个边的集合怎么样可以黑白染色,显然是一个二分图。
那么问题就变成了求最大权生成二分图。
由于点数只有 (26 - 7 = 19) 个,可以暴力枚举一组点集暴力更新答案 (O(n^2 2^n)),也可以递推做到 ((n2^n ))。
[NWRRC2014] Kebab House
即选出一个 01 串((0) 代表做梦),满足:
- 相邻的 (0) 之间的 (1) 的数量 (> t)
- 每段的 (1) 总和满足限制
不妨按段 (DP)。
维护一个 (f_i) 表示之前最后一个 (0) 的位置距离当前第一个位置的距离,前面满足条件的方案数。把 $ > t + 1$ 的方案记录在 (f_{t +1}) 上,因为转移的限制相同。
转移时,只需考虑新段最后一个 (0) 的位置,再枚举前面的 (f),之后中间的方案数对应的是在连续的一段选 (0),并且 (0) 的个数 (le q-x) 的一个方案数,乘起来。
考虑预处理出连续的一段选 (0) 的方案数,可以再设一个 DP,(g_{i,j}) 表示长度为 (i),满足限制的情况下选了 (j) 个 (0) 的方案数,最后一个位置的 (0) 为 (i)。这个可以直接 (O(q^3)) 枚举上一个 (0),DP 出来。
然后整个 DP 大概可以用 (O(ntq)) 算出来。
可以通过此题。
[CERC2017]Cumulative Code
乱 搞 带 师
我这个破东西的理论复杂度是 (O(2^{14} imes k^2+ q(2^{22} + k))) 的,不知道咋过去的。。
考虑暴力,就是看删除的顺序,如果是最右边一条链,是左根右,否则是左右根,因此迭代递归下去就行了,一次复杂度上界大概是元素个数 ( imes k)。
然后考虑优化,一般这样的东西可以根号分治,考虑 (d > B) 的东西,不会访问超过 (frac{2^k}{B}) 个点。
然后较小的 (d) 可以记忆化,因为我比较菜,只观察出了同一层,被整个区间包含,(d, a) 都相同的点可以记忆化,不过需要记忆一个 (k) 的数组,第 (k) 维代表深度为 (k) 的点有多少计入的答案。这样的化,相同的这些点就可以通过编号的偏移量来实现 (O(k)) 获得答案(在记忆化一遍以后),然后考虑不被包含的一层至多有左右两个,这个东西需要开 ((Bk)^2) 大小的数组,非常 8 行,然后取 (B = 2^7) 的时候才能开下。。
事实上正解貌似就是纯粹的根号分治,但是我不会记忆化www
后来发现所以这样记忆化没必要,因为最多就 (300) 个不同的 (d)。
所以数组实际上开 (Bk^2) 就行了。单次时间复杂度还是 (O(k + k^2B + frac{2^k}{B})) 因此取 (B = k imes 2^{k/2}) 最优。。
[CERC2014]Pork barrel
思维难度比较简单,毕竟我都想出来了。。
考虑从大到小维护一个 (ge v) 的边的最小生成树的所有边,从大到小考虑插入边,如果成了一个环就要扣掉原来最大的边。这个东西点边转化后用 LCT 维护。然后插入删除边对应在线段树对应位置加减权值,查询时只需在对应时刻,查询区间和即可。
但是要强制在线,上主席树。
每条边最多会被加入删除一次,因此复杂度 (O(T(n + (m + q) log m)))。
第一次写 LCT 练习题发现之前自己的板子是假的。。
[NEERC2014]Improvements
想了很久很久,最后好家伙看错题了。
相交指的是边相交。。
问题可以转化为每次在一个位置 (p),选择一个方向走若干距离,然后在新的位置立一个标记(即第 (i) 次的位置),途中不能跨越之前的标记(可以走到之前的)。
然后这样的一个东西一定就是在当前范围内一直走,要么就是回头,然后把范围分成了更小的。
把一个合法状态每个位置所在点编号写出来,发现是一个单峰序列,显然也比较好证明,每次标记只会在最内层制造一个比之前都大的编号,当然是单峰的了。
因此问题变为了改变至少若干编号的位置,让这个东西变成单峰的。
每次改变最多让最大单峰长度 (+1),而且可以做到。
因此能不改变的最大长度就是原序列的最大单峰子序列,正反跑 LIS 拼起来就行了。
[ICPC2014 WF]Metal Processing Plant
判定性问题比较好做,可以 (O(n^2)) 做 2-SAT。但这样复杂度是 (O(n^6))。可以枚举一条最大边,然后二分次大边(固定了最大边以后符合条件次大边的值显然是一个后缀),这样是 (O(n^4 log n )),双指针可以把 (log) 去掉(然后再乱搞一下可以卡常过此题)
然后的奇偶环优化非常的神仙,考虑从大到小枚举最大边,然后把之前所有边构成的联通块用并查集维护:
- 如果构成了奇环,考虑染色后,必要条件就是在现在的图上边没有相邻的颜色,但此时已经有奇环了,所以必然不可能存在最大边 $< $ 当前边的情况,可以做完一次直接 ( ext{break})
- 如果构成了偶环,那么你考虑要用到这个作为一个集合之内最大值,用到它就说明两端要同色,但这两端又隔奇数个点,所以没法做到这点,理由类似奇环,可以 ( ext{continue})
- 否则二分次大边做一遍
这样只会做 (n log n) 次 2-SAT(别忘最大边可以是 (0)),总复杂度是 (O(n ^3 log n)) 可以通过。
[NWRRC2014]Fragmentation
考虑降序的必须断开,离散化后不连续的必须断开,序列就变成大概是若干递增串(称之为块,给这样的每个块编个号qwq)的形式,然后可以连起来的段要满足的性质(充要):
- 如果三个以上不同数值连起来,被包含在中间的数必须恰好所有同数值都在这个段里,否则就拼不进来了。
- 离散化后,连接 ((x, x+1)) 这样的东西最多连一次。
如果假设最开始全断的化,要最大化连的次数。
这个东西似乎很难做,每段并不是独立的,看样子需要按数值顺序做安排。
想到(我就想不到呜呜呜)如果 (le x) 要如何连都安排好了,那么后续再连,前面对后面决策的可行性有影响的只有 ((x - 1, x)) 有没有连,连的哪个块:
- 如果 ((x, x+1)) 不准备连那么不影响
- 若 ((x, x +1)) 连的块不是 ((x - 1,x )) 的块,那么显然咋搞都行
- 如果是相同的块,那么就要检查是否 (x) 全部出现在这个块作为可行的要求。
因此我们可以设一个 DP:
-
(f_{i, j}) 表示连接到数值 (i),((i - 1, i)) 连的块编号是 (j)(没连搞成 (0)),最大化连的次数。
-
转移就枚举 ((i - 2, i - 1)) 连的块是啥
-
(f_{i,0}=max(f_{i-1,j}))
-
(f_{i, j} = max_{k ot= j}(f_{i - 1, k} + 1))
-
同一块内的转移要满足 (i - 1) 全部出现在这个块里,式子也是同上。
-
别看是二维 DP,但实际的状态数应该是 (O(n)) 的,因为每一个状态都一一对应着一个块内连续对。
然后考虑转移,暴力转移是 (O(n^2)) 的。优化也挺显然,前后缀 (max) 再加自身转移特判,每次转移做到 (O(1))。(具体实现来回扫一遍双指针)
总复杂度 (O(n))。
[ICPC2014 WF]Pachinko
设 (f_{i, j}) 为球到这个位置的概率,不难列出非障碍物数量级的有后效性的方程组,裸高斯消元解是 (O((nm)^3)) 的,但是这个方程组每组只有 (5) 个数系数非 (0),并且在长度为 (2m) 的连续的一段 ([i-m, i+m]) 内。
考虑高斯消元的过程,每次把前面的都消掉,然后把后面的影响加上。
那么每次操作过后,任意一个方程组的系数仍然在 ([i - m, i +m]) 的范围内。
这样就可以开一个 (O(nm^2)) 的数组记录系数,每次操作的影响也变少成了 (m) 组。
因此总复杂度 (O(nm^3))。
(此题貌似无法约旦,系数会偏移)
至今仍不清楚为什么此题有的地方解不出来。
[CERC2017]Buffalo Barricades
这题的关键想法是,现把终状态每个点唯一占据的数量算出来 ①,再加上每个牛被算多次的贡献 ②。
这样讲占据的总和变成 (n) 级别的。
步骤 1
你考虑一个人向左向下圈地的过程,比如向下,仅当他碰到了比他更靠右更靠下,且时间比他早的这样个东西,他就会停止。
那么你可以做一个扫描线,从右往左,维护每个人的纵坐标在一个 (set) 里。
这个 (set) 的意义表示,一个人管辖的纵坐标范围是其在 (set) 到他的前驱。
考虑加入一个人的影响,如果这个人前驱的时间大于他,那么这个前驱就会被这个人的栅栏挡住,在后续 (le x) 的过程都不会有他占领的范围了,可以删掉,反之则这个人被前驱挡住,保留。
这样就维护了一个纵坐标管辖范围,对于每个牛,就在 (set) 上找到对应管辖的人 ,,lower_bound 一下就行。
注意到最多删 (m) 次,因此复杂度 (O((n + m) log m))。
步骤 2
考虑 (a, b) 两个人,何时 (b) 占据的牛会对 (a) 占据的牛产生贡献,必须满足:
- (a) 的范围完全包含 (b)
- (a) 的时间比 (b) 早。
- (a, b) 中间的点时间都比 (a) 晚。
我们把 (a, b) 之间没有点,即 (b) 是最小包含 (a) 的点这样的东西,连一条 ((b, a)) 的边,形成了一个树形关系。
这个边也很好找,就在步骤 (1) 中,事实上在那个时刻把这个人当做一个牛,找包含他的店就好了。
那么 (b) 对 (a) 有贡献当且仅当 (b) 是 (a) 的儿子,并且 (a, b) 之间的点不包含 (a) 出现时刻都比 (a) 早。
那么算法便很好设计了,考虑时间倒流,处理到 (a) 时,把时间 (>) 的点全跟父亲连起来,(a) 的答案就是当前所在联通块的权值。
只有合并联通块的操作可以并查集。
[NEERC2015]Cactus Jubilee
-
如果断掉的是树边,两个点分别在断成的两个联通块上,否则不能联通。这部分在一遍 dfs 中很好算出。
-
如果断开的是环边,加上还是仙人掌,即选择的两点之间在断开的新图上都是树边。那考虑只保留树边。答案就是每个联通块任选两个的和。那你考虑选择的环边产生的新图,只是把这个环上延伸出去的树边加上这个环上的点,剩下的部分没有变。而且一个环上断边上形成的各块大小一致,所以可以一起算。因此每次枚举一个环,然后考虑断一条边合并了若干原图上的桥,计算一下贡献即可。复杂度是线性的。
[ICPC2019 WF]Karel the Robot
是不是就模拟就好了。
考虑记忆化一下,记录 (f_{x, y, d, c}) 在 ((x, y)) 方向 (d) 执行命令 (c) 最终会走到哪里,状态数是 (4rcd) 每次转移大概是 (100) 次。
永不停止并不可怕,只要在记忆化搜索的过程中走到相同的状态即可,怎么记录这个东西呢?考虑用逛公园的思想,把当前搜索的 dfs 树用 vis 赋值一下,如果又走到的就 (-1)。
后来发现因为有 u 的存在,这个复杂度大概是不对的,正解还要记忆化每次执行命令到哪里了。
这样才能严格的控制在 (4rc imes 36 imes 100 le 2 imes 10^7) 的计算量级。
反正他过了,而且没被卡,就不管了。
[NEERC2014]Epic Win!
我发现集训队的题都好好玩啊。
现考虑如果我们知道了对方当前状态,我们就可以建立一个 (n) 的节点,称之为一个系统,第 (i) 个节点设定为打败对方 (i) 号节点的状态,并且随之转移到下一个点,对着让他失败着卡就行了(
但是我们不知道,咋办呢?
一种想法是通过有限次弄到一个确定的位置,但显然他的构造(也许有循环性等等)不能让我们的统一化成立。
考虑利用模拟退火类似的方式,我们可以做 (500) 个系统,每次随机一个猜当前状态的位置,一旦失败,就转移到下一个系统,这样失败的概率在 (n = 100) 时是 ((frac{99}{100})^{500} approx 0.6 \%),这个东西感觉非常的小,基本不可能发生。
复杂度大概就是 (O(500n))
[NEERC2014]Hidden Maze
即要算出随机树,任意奇数路径中位数期望。
拆成和除以总数,总数可以用简单的树形 DP 在 (O(n)) 内得到(考虑在 LCA 那里算一下贡献)
考虑所有情况中位数的和怎么做。
那个随机树的性质告诉我们树的深度大概非常小。
考虑算每条边作为中位数的贡献,考虑当你确定的中位数(为了避免数值相同贡献的叠加,同值可以人为规定一个顺序),把 $< $ 这条边视为 (-1),(ge) 大的边视为 (1),即找到经过这条边且权值和为 (1) 的数量就是这条边的贡献。
为了让复杂度和深度有关,我们考虑设一个 DP,设 (f_{i, j}) 为 (i) 节点向下的一条路径,且在目前状态边权和为 (j) 的数量,由于深度很小,因此 DP 数组不会很大。
考虑把边权排序,然后从小到大考虑每条边贡献,目前需要做的事是:
- 统计经过这条边边权为 (0) 的数量。
- 将这条边权从 (-1) 变成 (1) ,改变对应的 (f) 数组。
我们发现只有祖先的 (f) 值会改变,因此做到了一次深度级别。具体实现时,我们可以先把祖先到这条链的答案消除,然后枚举第一步贡献的 LCA 手动计算一遍,再设一个类似的 DP 数组算出仅考虑经过这条边的一些 DP 值,然后把两个合并贡献进答案里,之后再更新祖先的 DP 值。
这样复杂度大概每次是深度的平方,但是可以通过(
设深度为 (d) 大概上界是 (O(nd^2))。
不知道为啥只能过评测鸭,不能过 CF(我太慢了。
[NEERC2016] Indiana Jones and the Uniform Cave
一道神仙题,首先只给我们三个状态,而且提到强连通,因此可以将石头的状态分别代表节点的三个状态:未访问 Center / 在当前栈(同 tarjan 算法)Right / 已经访问完毕 Left
然后考虑进行类似 Tarjan 的算法,当前到根的 DFS 树代表正在处理访问完所有边的点,让当前石头所在的通路位于当前考虑到的最后一次访问的边。对于每一个点总体执行 (m) 次 1 right 1
,然后我们需要考虑的是:走到一个非新点如何回来,以及这个点处理完毕如何回到父亲。
- 考虑如果走了一条边 ((u, v)),(v) 的状态是 Right,即在栈上,即是 (u) 的祖先,那么沿着当前的路
0 left 0
直到走到第一个为 Left 点,这期间经过的点数设为 (y),那么即 (v Rightarrow u) 在 dfs 树上有 (y) 个点,那么我们只要走 (y - 1) 次,每次都0 right 0
,就回到了 (u),并且还原了状态。 - 如果走到 ((u, v)),(v) 的状态是 Left,即已经处理完毕了,我们要尽量变成 1 的步骤,即尽量走到栈里,考虑这是一个强连通图,那么必然有 (low_u < dfn_u),我们每次只要走到自己那个更新自己 (low) 的走就一定能走到根节点。之后的问题同 1。即走两次 1、2 的环路。
- 如果 (u) 处理完毕,要回到 (fa_u),差不多同理 2,现走到祖先链然后同 1 跑到 2 即可。
在 2、3 补中我们需要这样的一个操作,在 Left 节点让他可以走到让他 low 小的点。那么我们只要 DFS 结束时让石头指向这个位置即可。考虑根据 Tarjan 算法类似进行处理即可,注意一个大坑就是无论此时在不在栈中都可以用 dfn 取更新,这是因为我们并没有真正的进行缩点,因此此处的部分有可能可以帮我们连上去(。
覆盖一条边的代价不会超过 (2n) 次,总次数 (2n^2m = 16000) 可以通过。
[CERC2014]Mountainous landscape
如果给我们一个区间 ([l, r]),我们要看这里面的点有没有可能作为答案,即 ([l, r+1]) 区间有没有折线有部分在该线上方,这个很容易做,可以在预处理这一段的上凸包(越靠上越好,那么被完全包含在内测的凸包的确是没有用的)后 (O(log n)) 得出,即二分出斜率大于当前线斜率的第一个折线右端查是否在这个线上方即可(还有一些细节,如没有大于的就是第一个点)。
因此我们可以建一棵线段树,每个节点预处理一个上凸包。
对于每个点,在线段树上二分即可。时间复杂度 (O(n log^2 n)),空间复杂度 (O(n log n))。
[ICPC2014 WF]Messenger
无解判断显然是如果从 A 起点出发直接走直线最快都无法截断(在 B 到终点前到达)。
直接做不好做(无法刻画距离时间间隔对等的关系),考虑二分答案 (x),判断邮递时间是否存在 (le x)。
问题等价于,A 从起点出发,B 从原线路走到 (x) 时刻出发(时刻对齐),是否有一个时刻使两者的距离 (le x)。证明: 如果满足,显然存在(不断缩小间隔让距离和间隔相同即可)。反过来如果存在一个答案 (ans le x),你可能通过拉长时间间隔让他们变成 (x),由于然后他们距离的变化不会超过时间间隔(你考虑两点 (a, b) 之间的距离,当 (a) 移动 (d) 长度,距离变动会小于等于 (d)) ,所以可以得到一个时间间隔是 (x) 然后距离 (le x) 的答案。
我们将时间段按照 (n + m) 个两人走的转折点分成若干段,每段 A、B 都是在一条直线上做匀速直线运动。
运 动 是 相 对 的。
可以转化为 A 静止,B 匀速直线运动。
即求 A 在 B 运动线段轨迹距离最小值,即点到线段最小值,就能做了。
时间复杂度 (O(n log n))。
[ICPC2019 WF]Directing Rainfall
口胡假了,还可以滚到一半挖洞,我自闭了。
考虑动态维护这样一个 (f) 数组,(f_i) 表示当前从 (i) 位置最后到达葡萄园最少打洞数量。初始即 ([l, r]) 是 (0),剩下是正无穷。
从下到上考虑加入一条线段的影响,设这个线段横跨的区间是 ([a, b])
- 如果左边高,那么对于 (a < i < b),会使 (f_i = min( (min_{i le j < b} f_j) + 1, f_b))
- 如果右边高,那么对于 (a < i < b),会使 (f_i = min((min_{a < j le i} f_j)+1,f_a))
相当于先在 ((a, b)) 区间加,然后在 ((a, b]) 区间做一个后缀 (min), 或在 ([a, b)) 做一个前缀 (min)。可以在线段树上维护这个东西,记录每个节点是否单调,以前缀 (min) 为例,从左到右遍历每个 (log) 个极大被包含的区间,记录一个阙值 (v) 代表左侧已访问过的最小值,如果这个值大于当前节点最小值就区间覆盖,如果大于最大值并且这段单调递减,就直接阙值为该节点的最小值然后退出,否则递归左右子树。
这样每访问一个节点可以让他变成单调的,区间加只会让 (log) 个节点不单调,大概是 (O(n log n))(俺也不会分析 /kk)
那如何从下往上排序呢?
我们要达到类似这样的排序效果,要满足任意一条竖线从下往上都是递增的。
横着做一遍扫描线,我们弄一个 set 动态维护扫描线上的编号序列,只要他们都在这条线上有交,那么必然有顺序。我们插入一条线看起点这个位置的前驱后继连一条边就行了。证明:所有边都是合法的,考虑相邻的两条线段一定存在一条有向边,因此是 ok 的。