HDU2022多校联训
2022-07-19
String
用 \(\text{exkmp}\) 求出 \(S\) 串和它的所有后缀的 \(\text{lcp}\),那么我们令 \([1,n]\) 和 \([i,n]\) 这段 \(\text{lcp}\) 长度为 \(x\) ,当满足 \(i\le x\) 时,对于 \(\forall j,j\times k\le x-i+1\) ,都会在 \((i-1)\times 2+j\times k\) 产生贡献,因此做一个模意义的差分即可。
点击查看代码
Dragon slayer
墙的数量只有 15,爆搜或者状压都能比较轻松的通过。
点击查看代码
Backpack
\(dp[i][j][k]\) 表示前 \(i\) 个物品,异或和为 \(j\),并且体积为 \(k\) 的方案是否存在。
有:
可以用 \(\text{bitset}\) 优化,\(dp[i][j]=dp[i-1][j]\vee(dp[i-1][j\oplus w_i]<<v_i)\) 。
Ball
考虑枚举一下三个点,我们可以分别计算距离并进行判断。
但这样子时间复杂度太高,考虑枚举两个点,如何确定第三个点,我们即要求其中一条边小于等于这条边,另一条边大于等于这条边这可以利用两个 \(\text{bitset}\) 解决。
点击查看代码
Grammar
Travel plan
由于不存在长度为偶数的简单回路,所以任何两个简单回路不存在公共边,于是这个图就是仙人掌图。
求最大公因数为 \(i\) 的路径,可以用莫比乌斯反演转化为求 \(i\) 的倍数的路径。
这样对只需要对 \(n\) 个只包含 \(i\) 的倍数的边的图计算简单路径数量,仙人掌图上的简单路径数量可以 \(\text{dp}\) 求得。
点击查看代码
Treasure
首先建出并查集重构树(在最小生成树的基础上,每次新增一条边就加一个新点连向合并的两个集合),对于一个询问,我们利用倍增跳到对应的节点上,那么问题就变成了查询一个子树。
那么对于这一棵子树,我们需要查询子树里每种类型的最大值并求和。
因为每种类型的点最多只有 \(10\) 个,我们在重构树的每个点维护子树每种类型最大值的和,那么可见对于一次修改,实际只需要做不超过 \(10\) 次链上加操作,因此只需要像建立一个类似虚树的结构,每次询问从修改点暴力往上跳并且做链上修改即可,这个过程可以用树状数组维护(链上加单点求值在树上差分后变成单点修改子树求和),总时间复杂度 \(O(10\times n\log n)\)。
点击查看代码
Path
首先对于上一步是不是特殊边我们可以考虑建一个分层图,上面一层代表上一步是,下面一层代表不是。
注意到如果没有 \(0\) 的边这个题就是普通的最短路。
考虑走到上一层之后我们应该怎么走 \(0\) 的边,我们可以把没访问过的点加入一个 \(\text{set}\) ,并取出所有点并判断这些点是否存在于当前点出边集合,如果不存在就将其最短路更新,更新成功则入堆。
点击查看代码
Laser
首先如果所有点都在同一条水平/竖直/斜线上,那么可以直接输出。
假设我们已经确定一个点在水平方向上(米字的一横),那么随便找一个不在这条直线上的点,用竖线和两条斜线可以交在这条线的三个点上,只需要暴力判断这三个点作为中心点是否合法即可。
对于更普遍的情况,我们只需要每次将坐标旋转 \(45\) 度,将上面的过程重复 \(4\) 次即可,这样可以讨论到所有情况。
或者可以直接找到两个不共线的点,讨论两条线的相交情况,做 \(12\) 次判定也是可以的,时间复杂度 \(O(n)\)。
点击查看代码
Walk
Random
答案为 \(\dfrac{n-m}{2}\) ,直接输出即可。
点击查看代码
Alice and Bob
将每个值为 \(i\) 的数字视为 \(2^{n-i}\),那么 \(\text{Alice}\) 的胜利条件就是最终局面中能出现 。
\(\text{Alice}\) 将数字分成两个集合,\(\text{Bob}\) 将其中一个集合减一,就相当于将这个集合中的数全部乘 \(2\),然后将另一个集合删去。
如果 \(\text{Alice}\) 能将集合中的数字按照值二等分,那么无论 \(\text{Bob}\) 怎么操作,黑板上所有数字的总和实际是不变的。
如果集合中的数字总和超过 \(2^n\),由于所有数字都是不超过 \(2^n\) 的 \(2\) 的幂次,那么 \(\text{Alice}\) 的每次分割总能使得两边集合的值均不小于 \(2^{n-1}\)。
因此直接判断所有数字的 \(2\) 的幂次的总和即可。
点击查看代码
2022-07-21
Static Query on Tree
树剖或建虚树都可以,标程做法如下:
考虑一个简化的问题 \(1\),如果只有 \(A\) 的限制,也就是求一个集合里所有节点可以走到的节点数。首先对 \(A\) 根据节点在 \(\text{dfs}\) 序中的位置排序,然后将深度加起来,减去相邻节点 \(\text{lca}\) 的深度之和。
在考虑一个稍复杂的问题 \(2\),如果只有 \(A, B\) 的限制。很显然这个问题即两个问题 \(1\) 的交集,但是直接算交集比较困难。我们有公式,两个集合大小之和等于交集大小加并集大小,因此只要算出 \(A, B, A\cup B\) 在问题 \(1\) 的解,就能推断出并集的大小了。
最后就是原问题,其实就是分成了多个子树,在这些子树上进行问题 \(2\) 的操作。
点击查看代码
C++ to Python
签到,只要无视字母、下划线、冒号后输出即可。
点击查看代码
Copy
可持久化平衡树即可,标程做法如下:
一个修改操作对后续的查询操作的影响:如果 \(x\le r\) 就没影响,如果 \(x>r\),相当于查询 \(x-(r-l+1)\),然后去掉修改操作。
因此可以离线,倒着处理所有修改操作,每个修改操作都让它之后的所有 的询问 \(x -= r - l + 1\) 。
但是这么处理还是 \(O(n^2)\) 的。考虑到我们只要答案的异或和,就有,两个相同的查询可以抵消,因此同一位置至多只会查询一次。这样每个位置用 \(1\) \(\text{bit}\) 的信息表示即可,也就是 \(\text{bitset}\) 。
我们令 \(\text{dp}\) 第 \(i\) 位为 \(1\) 表示答案需要对 \(a[i]\) 异或。倒着遍历所有操作,如果是查询操作,\(dp[x] ^= 1\) ,如果是修改操作,那么就让 \(r+1..n\) 这些比特右移 \(r-l+1\),代码如下:
low = dp & (~bitset<N>(0) >> (N - r - 1));
high = dp & (~bitset<N>(0) << (r + 1));
dp = low ^ (high >> (r + 1 - l));
点击查看代码
Keychains
显然关键问题是如何求圆 \(A\) 和 圆 \(B\) 所在平面的两个交点。得到这两个交点后,只要判断是否一个点在圆 \(B\) 内部,一个点在圆 \(B\) 外部(到圆心的距离小于半径、大于半径),就是答案了。
直接算圆和平面交点似乎不太好做,考虑到这两个交点肯定在两个圆所在平面上,因此先求这两个平面的交线,然后求交线和球 \(A\) 的交点(假设球 \(A\) 的圆心和半径就是圆 \(A\) 的半径)。
直线和球的交点的求法,和平面几何的直线和圆交点的求法类似。
点击查看代码
Slayers Come
显然,每个技能都可以击败一个区间的野怪,我们先处理出每个技能可以击败的区间。
先计算区间的右端点:将所有的 \(a[i]-b[i+1]\) 从大到小排序,再将所有的技能按 \(R[j]\) 从大到小排序,依次扫描每个技能( \(R[j]\) 相同的一起处理)。对于 \(a[i]-b[i+1]\ge R[j]\) 的所有 \(i\),则用并查集将 \(i\) 和 \(i+1\) 合并。第 \(j\) 个区间的右端点就是 \(X[j]\) 所在连通块的最右值。
左端点同理。
接下来就是区间重复覆盖计数问题:\(n\) 个位置,\(m\) 个区间,求选出的区间能够重复覆盖 \([1,n]\) 的方案数。
设 \(dp[i]\) 表示恰好覆盖了区间 \([1,i]\) 的方案数。
我们将所有区间按照右端点从小到大进行排序,依次扫描每个区间,考虑一个区间 对 数组的贡献。
用单点加法,区间乘法,区间求和的线段树维护 \(\text{dp}\) 数组即可。
Bowcraft
Snatch Groceries
题目问置信区间重叠前有几个人成功抢到菜。
按关键词为 \(earliest\) 升序排序后,比较相邻两个,如果 \(latiest_i\ge earliest_i\) 则有重叠。
证明。假设前面都没有重叠,那么 \(i\) 之后最有可能和 \(i\) 重叠的一定是 \(earliest\) 最小的,反之如果最小的都没有重叠,那么之后必然不可能有一个区间与 \(i\) 重叠。所以这样排序处理没有问题。
点击查看代码
Keyboard Warrior
使用哈希算法来比较两个字符串是否相同。
我们对字符串进行压缩,把连续相同的字符压缩为 \(\{ch,k\}\) 表示 \(k\) 个字符 \(ch\)。
可以用栈来维护当前缓冲区字符。
比较时方便起见,可以将首位和末位独立考虑,如果字符相同数量大于等于目标文本即可。
点击查看代码
ShuanQ
\(M\) 是 \(kM\) 的一个比 \(P,Q\) 大的质因子。
最多只有一个质因子满足要求,如果有多个满足要求质因子 \(M_1,M_2\) 那么 \(kM=M_1\times M_2>P*Q\) 矛盾。
点击查看代码
Assassination
DOS Card
动态 \(dp\) 即可。
点击查看代码
Luxury cruise ship
当体积为 \(365\times 31\times 7\) 的倍数时,一定是全取 \(365\) 最优,背包处理出 \(365\times 31\times 7\) 的答案,每次将 \(n\) 分成除以 \(365\times 31\times 7\) 的商和余数,分别处理后加起来即可。
点击查看代码
2022-07-26
Equipment Upgrade
令 \(dp[i]\) 表示从 \(i\) 升到 \(i+1\) 的期望花费,有:
可以分治 \(\text{NTT}\) 求出,\(\sum_{i=0}^{n-1}dp[i]\) 即为答案。
点击查看代码
Boss Rush
二分答案,状压 \(dp\) \(\text{check}\) 。
点击查看代码
Cyber Language
小模拟
点击查看代码
Divide the Sweets
Spanning Tree Game
状压连通性,做背包,时间复杂度 \(O(m^2Bell(n))\) ,\(Bell(n)\) 表示 \(n\) 的拆分数。
点击查看代码
Dusk Moon
对于一个点集,它的凸包覆盖住了所有点,且最小覆盖圆覆盖住了凸包,因此仅保留凸包的顶点不会影响答案。
由于点的坐标在给定的正方形范围内随机,因此一个点集的凸包的期望顶点数为 \(O(\log n)\) ,使用线段树直接记录区间凸包点集,然后对于 \(O(\log n)\) 个点运行最小圆覆盖算法即可。
时间复杂度 \(O\left(q \log ^{2} n\right)\) 。
点击查看代码
Shallow Moon
Laser Alarm
三个不共线的点可以确定一个平面。对于任意一个平面,将其调整至经过三个顶点,结果不会变差。因此枚举三个顶点得到平面,然后 \(O(n)\) 计算触碰了该平面的线段数,更新答案即可。所有点都共线的情况需要特判。
时间复杂度 \(O\left(n^{4}\right)\) 。
点击查看代码
Package Delivery
考虑 \(r\) 最小的那个区间 \(k\), 第一次取快递放在第 \(r_{k}\) 天一定不会使结果变差。此时可能有很多区间覆盖了 \(r_{k}\), 那么为了尽量延后下一次取快递的日期, 此时的最优策略应该是选择覆盖 \(r_{k}\) 和 \(r\) 值最小的 \(k\) 个区间, 使用堆找到并去掉这些区间后, 问题就递归了。重复上述过程直至处理完所有 \(n\) 个区间。
时间复杂度 \(O(n \log n)\) 。
点击查看代码
Range Reachability Query
时限比较大, \(n,q\) 比较小,考虑 \(\text{bitset}\) ,令 \(dp[i][j]\) 表示从点 \(i\) 出发是否能到达询问 \(j\) 的答案,\(S[i][j]\) 表示边 \(i\) 是否能在询问 \(j\) 中使用,对于一条边 \(u_i\to v_i\) 有转移:\(dp[u_i]|=dp[v_i]\&S[i]\) 。
其中 \(S\) 可以扫面线求出,不过如果暴力存下每一个 \(S\) ,空间有些不够,可以分块,只存 \(\sqrt n\) 个关键点的 \(S\) ,每次用的时候从关键点处递推,时间复杂度 \(O(\frac{mq}{w}+m\sqrt m)\) ,不是复杂度瓶颈。
点击查看代码
Taxi
如果没有 \(w\) 的限制, 那么是经典问题。根据 \(|x|=\max (x,-x)\) ,有:
分别记录 \(x_{i}+y_{i}\) 、\(x_{i}-y_{i}\) 、\(-x_{i}+y_{i}\) 、\(-x_{i}-y_{i}\) 的最大值即可在 \(O(1)\) 时间内求出所有点到 \(\left(x^{\prime}, y^{\prime}\right)\) 的曼哈顿距离的最大值。
现在考虑加入 \(w\) 的限制。将所有城镇按照 \(w\) 从小到大排序, 并记录排序后每个后缀的 \(x_{i}+y_{i}\)、\(x_{i}-y_{i}\)、\(-x_{i}+y_{i}\)、\(-x_{i}-y_{i}\) 的最大值, 用于 \(O(1)\) 求给定点 \(\left(x^{\prime}, y^{\prime}\right)\) 到该后缀中所有点的距离最大值。
选取按 \(w\) 排序后的第 \(k\) 个城镇,\(O(1)\) 求出给定点 \(\left(x^{\prime}, y^{\prime}\right)\) 到第 \(k . . n\) 个城镇的距离最大值 \(d\) ,有两种情况:
-
\(w_{k}<d\) ,那么第 \(k . . n\) 个城镇对答案的贡献至少为 \(w_{k}\) 。用 \(w_{k}\) 更新答案后,由于第 \(1 . . k\) 个城镇的 \(w\) 值均不超过 \(w_{k}\) ,因此它们不可能接着更新答案, 考虑范围缩小至 \([k+1, n]\) 。
-
\(w_{k} \geq d\) ,那么第 \(k . . n\) 个城镇对答案的贡献为 \(d\) 。用 \(d\) 更新答案后, 考虑范围缩小至 \([1, k-1]\) 。
容易发现每次考虑的范围都是一个区间, 如果每次取 \(k\) 为区间的中点, 那么迭代 \(O(\log n)\) 次即可得到最优解。
时间复杂度 \(O((n+q) \log n)\) 。
点击查看代码
Two Permutations
首先特判序列 \(S\) 中每个数字出现次数不都为 \(2\) 的情况, 此时答案为 \(0\) 。
动态规划,设 \(f_{i, j}\) 表示 \(P\) 的前 \(i\) 项匹配上了 \(S\),且 \(P_{i}\) 匹配 \(S\) 中数字 \(P_{i}\) 第 \(j\) 次出现的位置时,有多少种合法的方案。由于 \(S\) 中每个数字出现次数都为 \(2\) , 因此状态数为 \(O(n)\) 。转移时枚举 \(P_{i+1}\) 匹配哪个位置, 那么 \(P_{i}\) 匹配的位置与 \(P_{i+1}\) 匹配的位置中间的那段连续子串需要完全匹配 \(Q\) 中对应的子串, 使用字符串 \(\text{Hash}\) 进行 \(O(1)\) 判断即可。
时间复杂度 \(O(n)\) 。
点击查看代码
2022-07-28
Link with Bracket Sequence II
区间 \(DP\),设 \(f_{i, j}\) 表示 \([i, j]\) 为合法括号序列且 \(i, j\) 上括号相互匹配的方案数,\(g_{i, j}\) 表示 \([i, j]\) 区间形成一 个合法括号序列的方案数,转移为:
-
枚举 \(i, j\) 位置上填写的内容,如果形成匹配的括号对,则:\(f_{i, j}=k * g_{i+1, j-1}\) ,其中 \(k\) 为使得 \(i, j\) 上括号相匹配的方案数。
-
一般地,\(g_{i, j}=g_{i, k}+f_{k+1, j}\) 。
答案取 \(g_{1, n}\) 即可,复杂度 \(O\left(n^{3}\right)\) 。
点击查看代码
Link with Running
在最短路图上跑最长路。
-
先用 \(\text{dijkstra}\) 在第一个权值上跑出最短路图来,并求出第一个答案(最短路图的边权为第二个权值)。
-
注意到第一个权上可能为 \(0\) ,因此它并不一定是个 \(\text{DAG}\),需要用 \(\text{trajan}\) 将强连通分量缩起来。
-
获得了 \(\text{DAG}\) 之后,只需要在 \(\text{DAG}\) 上求最长路即可得到第二个答案。
点击查看代码
Magic
Link with Equilateral Triangle
输出一堆 \(No\) 即可。
证明:
对于一个合法的解,应当满足不存在同时包含 \(0,1,2\) 的三角形,下面我们证明这样的三角形一定存在。
左下角必然是 \(1\) ,右下角必然是 \(0\) ,底边不能含有 \(2\),则底边上必然有奇数条 \(1-0\) 的边,这些边都属于一个小三角形。考虑其他的 \(0-1\) 边,由于不在两个斜边上,其他的 \(0-1\) 边必然属于两个三角形。因此 “每个三角形内 \(0-1\) 边的数量" 的和必然为奇数。
但是,假设不存在 \(0-1-2\) 的三角形,则所有三角形都必然包含 \(0\) 条或 \(2\) 条的 \(0-1\) 边,产生了矛盾。
因此一定存在 \(0-1-2\) 的三角形。
点击查看代码
BIT Subway
签到题,阅读理解题。
真实的情况很容易算,\(DLee\) 的价格实际上是一个关于总原票价 \(x\) 的分段函数:
点击查看代码
Climb Stairs
由于题目要求必须把所有怪物打完,所以跳过的怪物还必须得走回去打败才行。
我们要从当前位置 \(x\) ,找到一个右端点 \(r\) ,使得可以按照 \(r, r-1, r-2, \ldots, x+1\) 的顺序打败这一段的怪物。
不难看出 \(r\) 更小的时候不会更劣。直接暴力维护当前想要先到达的右端点(满足 \(x+k \leq r\) ),用线段树维护一下 \([l, r]\) 的更新位置后,相当于实际上完成了击杀 \(r, r-1, \ldots, l\) 的过程,所以将后面的区间加上相应的值。由于每个右端点只会被计算一次,所以时间复杂度是 \(O(n \log n)\) 的。
点击查看代码