联合省选 2021 解题报告
很久没有写过博客了呢。
文章目录
- D1T1 - 卡牌游戏
- D1T2 - 矩阵游戏
- D1T3 - 图函数
- D2T1 - 宝石
- D2T2 - 滚榜
- D2T3 - 支配
- B卷D1T1 - 数对
- B卷D2T1 - 取模
- Summary
- 技巧类
- 代码类
D1T1 - 卡牌游戏
Alice 有 nnn 张卡牌,第 iii(1≤i≤n1 \le i \le n1≤i≤n)张卡牌的正面有数字 aia_iai,背面有数字 bib_ibi,初始时所有卡牌正面朝上。
现在 Alice 可以将不超过 mmm 张卡牌翻面,即由正面朝上改为背面朝上。Alice 的目标是让最终朝上的 nnn 个数字的极差(最大值与最小值的差)尽量小。请你帮 Alice 算一算极差的最小值是多少。
对于数字考虑,极差最小就是对于排序之后的数组中,取一个长度最短的区间。
我们把 aia_iai 和 bib_ibi 都放进去排序,取 nnn 张牌至少出现一次且反面向上的不超过 mmm 张的区间。这很好维护,也不难证明左端点移动时,右端点是单调的,因此用双指针维护即可。
时间复杂度 O(n)\mathcal{O}(n)O(n).
D1T2 - 矩阵游戏
Alice 有一个 n×mn \times mn×m 的矩阵 ai,ja_{i, j}ai,j(1≤i≤n1 \le i \le n1≤i≤n,1≤j≤m1 \le j \le m1≤j≤m),其每个元素为大小不超过 106{10}^6106 的非负整数。
Bob 根据该矩阵生成了一个 (n−1)×(m−1)(n - 1) \times (m - 1)(n−1)×(m−1) 的矩阵 bi,jb_{i, j}bi,j(1≤i≤n−11 \le i \le n - 11≤i≤n−1,1≤j≤m−11 \le j \le m - 11≤j≤m−1),每个元素的生成公式为
bi,j=ai,j+ai,j+1+ai+1,j+ai+1,j+1b_{i, j} = a_{i, j} + a_{i, j + 1} + a_{i + 1, j} + a_{i + 1, j + 1} bi,j=ai,j+ai,j+1+ai+1,j+ai+1,j+1
现在 Alice 忘记了矩阵 ai,ja_{i, j}ai,j,请你根据 Bob 给出的矩阵 bi,jb_{i, j}bi,j 还原出 ai,ja_{i, j}ai,j。
直接高斯消元可以获得一组没有限制的可行解。
首先我们考虑 ai,ja_{i,j}ai,j 范围没有限制的时候怎么做。
那就把第 1 行和第 1 列都定为 0 去递推就好了,反正一定有解,根本不要什么高斯消元。
那么我们就要考虑怎么把这组可行解的值都塞进 [0,106][0,10^6][0,106] 以内。
考虑将矩阵的一行加上 ⟨−k,k,−k,…,(−1)j⋅k,…,(−1)m⋅k⟩\left\langle -k,k,-k,\ldots,(-1)^j\cdot k,\ldots, (-1)^m\cdot k\ \right\rangle⟨−k,k,−k,…,(−1)j⋅k,…,(−1)m⋅k ⟩,那么显然 bbb 不会发生改变,因为受到影响的四格中必然一格加一格减。列同理。
所以我们可以将元素 ai,j′a'_{i,j}ai,j′ 调整为 ai,j=ai,j′+(−1)j⋅pi+(−1)i⋅qja_{i,j}=a'_{i,j}+(-1)^j\cdot p_i+(-1)^i\cdot q_jai,j=ai,j′+(−1)j⋅pi+(−1)i⋅qj,那么只要 ai,j∈[0,106]a_{i,j}\in[0,10^6]ai,j∈[0,106],即
−ai,j′<(−1)j⋅pi+(−1)i⋅qj<106−ai,j′-a'_{i,j}<\quad (-1)^j\cdot p_i+(-1)^i\cdot q_j\quad <10^6-a'_{i,j}−ai,j′<(−1)j⋅pi+(−1)i⋅qj<106−ai,j′
考虑解不等式的一边,那么就很显然是一个差分约束问题……好像哪里不对劲。
如果 pip_ipi 和 qjq_jqj 同号,也就是 i,ji,ji,j 奇偶性相同就没有办法做了。明明长得就是个差分约束的样子,却又不能做。于是做到这我自闭了……
于是就学习了一个新的技巧。
我们令 ci=(−1)i⋅pi,dj=(−1)j+1⋅qjc_i=(-1)^i\cdot p_i, d_j=(-1)^{j+1}\cdot q_jci=(−1)i⋅pi,dj=(−1)j+1⋅qj,则
(−1)j⋅pi+(−1)i⋅qj=(−1)i+j⋅ci+(−1)i+j+1⋅dj\begin{matrix}(-1)^j\cdot p_i+(-1)^i\cdot q_j&=&(-1)^{i+j}\cdot c_i+(-1)^{i+j+1}\cdot d_j\end{matrix}(−1)j⋅pi+(−1)i⋅qj=(−1)i+j⋅ci+(−1)i+j+1⋅dj
这时候 ci−djc_i-d_jci−dj 或者 dj−cid_j-c_idj−ci就可以直接做了。
这种解法的本质是,我们将整个矩阵黑白染色,若 (1,1)(1,1)(1,1) 为黑色,那么黑色的格子表示的就是 i,ji,ji,j 奇偶相同的情况,白色的格子表示的就是 i,ji,ji,j 不同的情况。
为了使格子中 ppp 和 qqq 的符号不同,我们在行采用从 (−1)j(-1)^j(−1)j 开始,每次取相反数;在列采用从 (−1)i+1(-1)^{i+1}(−1)i+1 开始,每次取相反数。也就是说,我们有意地使行列起点的符号不同,达到正负号错开的效果。
对于黑色格子来说,i,ji,ji,j 符号相同,那么改变之后,将恰好有一维(行或列)的符号被调整了。原来两个同号,改变之后两个就异号了。
对于白色格子来说,i,ji,ji,j 符号不同,那么改变之后,要么两个都不改变,要么两个都被改变了。原来两个异号,改变之后两个仍为异号。
这样一来,行和列的偏移量系数必然异号,就可以直接差分约束了。从结论来看非常简单,似乎是很 sb 的技巧,但是类似的思想还是值得学习的。
复杂度 O(nm(n+m))\mathcal{O}(nm(n+m))O(nm(n+m)),即 O(n3)\mathcal{O}(n^3)O(n3).
值得注意的是,这是一张稠密图,使用 spfa 与 bellman-ford 实际效果类似,需要选手自带小常数或者进行少量常数优化才能通过,否则存在一定的很大的卡常风险。
Update: 在实际测试中,我注意到在差分约束有解的时候,spfa 跑的非常快,但是,判断无解(负环)从理论上要求有一个点入队超过 nnn 次,这会将 spfa 的复杂度直接卡到上限。
也就是说,正常情况下满的 O(Tn3)\mathcal{O}(Tn^3)O(Tn3) 是不能够通过所有数据的,只会有 50 分的暴力分。我被迫采用了可能被卡到指数级的 SLF 优化通过本题。但是这是不严谨的做法。还有一种优化方式是入队次数超过一个阈值就直接掐掉,这同样也是不严谨的。还有使用 vector 存储,对边表 random_shuffle 之后似乎能比较稳定地通过而不被卡,但是这从复杂度上也难以证明其正确性。
总而言之,要想在规定时间内用严谨复杂度算法通过本题并不容易——当然由于数据强度并不高,随便加点优化就能获得 100 分。
D1T3 - 图函数
对于一张 nnn 个点 mmm 条边的有向图 GGG(顶点从 1∼n1 \sim n1∼n 编号),定义函数 f(u,G)f(u, G)f(u,G):
- 初始化返回值 cnt=0cnt = 0cnt=0,图 G′=GG' = GG′=G。
- 从 111 至 nnn 按顺序枚举顶点 vvv,如果当前的图 G′G'G′ 中,从 uuu 到 vvv 与从 vvv 到 uuu 的路径都存在,则将 cnt+1cnt + 1cnt+1,并在图 G′G'G′ 中删去顶点 vvv 以及与它相关的边。
- 第 222 步结束后,返回值 cntcntcnt 即为函数值。
现在给定一张有向图 GGG,请你求出 h(G)=f(1,G)+f(2,G)+⋯+f(n,G)h(G) = f(1, G) + f(2, G) + \cdots + f(n, G)h(G)=f(1,G)+f(2,G)+⋯+f(n,G) 的值。更进一步地,记删除(按输入顺序给出的)第 111 到 iii 条边后的图为 GiG_iGi(1≤i≤m1 \le i \le m1≤i≤m),请你求出所有 h(Gi)h(G_i)h(Gi) 的值。
咕。
D2T1 - 宝石
欧艾大陆上有 nnn 座城市,城市从 1∼n1 \sim n1∼n 编号,所有城市经由 n−1n - 1n−1 条无向道路互相连通,即 nnn 座城市与 n−1n - 1n−1 条道路构成了一棵树。
每座城市的集市上都会出售宝石,总共有 mmm 种不同的宝石,用 1∼m1 \sim m1∼m 编号。iii 号城市的集市出售的是第 wiw_iwi 种宝石,一种宝石可能会在多座城市的集市出售。
K 神有一个宝石收集器。这个宝石收集器能按照顺序收集至多 ccc 颗宝石,其收集宝石的顺序为:P1,P2,…,PcP_1,P_2,\ldots,P_cP1,P2,…,Pc。更具体地,收集器需要先放入第 P1P_1P1 种宝石,然后才能再放入第 P2P_2P2 种宝石,之后再能放入第 P3P_3P3 种宝石,以此类推。其中 P1,P2,…,PcP_1,P_2,\ldots,P_cP1,P2,…,Pc 互不相等。
K 神到达一个城市后,如果该城市的集市上出售的宝石种类和当前收集器中需要放入的种类相同,则他可以在该城市的集市上购买一颗宝石并放入宝石收集器中;否则他只会路过该城市什么都不做。
现在 K 神给了你 qqq 次询问,每次给出起点 sis_isi 与终点 tit_iti,他想知道如果从 sis_isi 号城市出发,沿最短路线走到 tit_iti 号城市后,他的收集器中最多能收集到几个宝石?(在每次询问中,收集器内初始时没有任何宝石。起点与终点城市集市上的宝石可以尝试被收集)
首先,由于 pip_ipi 是不重复的,我们可以知道每一个颜色之前应当是什么颜色,之后应当是什么颜色。
所以,我们在遍历整个树的时候,可以记录其祖先节点中,颜色为其下一个的最近节点是哪一个。根据这个,我们就可以用类似倍增 lca 的方法在树上倍增,快速定位一条路径上 u→lcau\to lcau→lca 的部分,至多可以收集多少宝石。显然,前半部分取得越多越好。
那么我们就只要考虑 lca→vlca\to vlca→v 的路径了。我们将询问放在一个并查集内,询问之间有连边当且仅当这些询问当前以及收集到的宝石数量相同。我们给并查集森林中的每一个连通块赋权。一个连通块的权值 xxx 代表着这个联通块内的所有询问,到当前位置,至多可以收集到 xxx 个宝石。
那么,对于同一连通块内的询问,由于当前最后一个宝石相同,于是他们的目的也相同。当我们带着询问往下遍历子树的时候,如果遇到了下一个颜色,整个集合内的答案都会增加 111. 之后,他们的目标就会和原来就在 x+1x+1x+1 的询问的目标保持一致,也就是说,权为 xxx 的连通块需要与权为 x+1x+1x+1 的连通块合并。
这样,当询问到达终点的时候,我们只需要查询这个询问所在的连通块的权值即可。
由于退出子树的时候需要按栈序撤销之前的合并操作,因此需要采用可撤销并查集,即不使用路径压缩、仅使用按秩合并维护并查集。
时间复杂度 O(nlog2n)\mathcal{O}(n\log_2 n)O(nlog2n).
D2T2 - 滚榜
封榜是 ICPC 系列竞赛中的一个特色机制。ICPC
竞赛是实时反馈提交结果的程序设计竞赛,参赛选手与场外观众可以通过排行榜实时查看每个参赛队伍的过题数与排名。竞赛的最后一小时会进行“封榜”,即排行榜上将隐藏最后一小时内的提交的结果。赛后通过滚榜环节将最后一小时的结果(即每只队伍最后一小时的过题数)公布。Alice 围观了一场 ICPC 竞赛的滚榜环节。本次竞赛共有 nnn 支队伍参赛,队伍从 1∼n1 \sim n1∼n 编号,iii
号队伍在封榜前通过的题数为 aia_iai。排行榜上队伍按照过题数从大到小进行排名,若两支队伍过题数相同,则编号小的队伍排名靠前。滚榜时主办方以 bib_ibi 不降的顺序依次公布了每支队伍在封榜后的过题数 bib_ibi(最终该队伍总过题数为
ai+bia_i+b_iai+bi),并且每公布一支队伍的结果,排行榜上就会实时更新排名。Alice
并不记得队伍被公布的顺序,也不记得最终排行榜上的排名情况,只记得每次公布后,本次被公布结果的队伍都成为了新排行榜上的第一名,以及所有队伍在封榜后一共通过了
mmm 道题(即 ∑i=1nbi=m\sum_{i=1}^n b_i=m∑i=1nbi=m)。现在 Alice 想请你帮她算算,最终排行榜上队伍的排名情况可能有多少种。
写在前面:数组的排列顺序有助于常数。
nnn 很小,每次付出的代价又是和顺序有关的,所以考虑状压 dp。
记 f(S,i,bi,j)f(S,i,b_i,j)f(S,i,bi,j) 表示当前选了集合 SSS,其中上一个人为 iii,用了 bib_ibi 道题目,累计已经滚了 jjj 道题目。
贪心地,bib_ibi 的选择一定会使当前这个队伍恰好超过之前的第一名。
因此,令 bk=max{bi,ai+bi−ak+[k<i]}b_k=\max\{b_i,a_i+b_i-a_k+[k<i]\}bk=max{bi,ai+bi−ak+[k<i]},则
f(S∪{k},k,bk,j+bk)←f(S,i,bi,j)f(S\cup\{k\},k,b_k,j+b_k) \gets f(S,i,b_i,j)f(S∪{k},k,bk,j+bk)←f(S,i,bi,j)
时间复杂度 O(2n⋅nm2)\mathcal{O}(2^n\cdot nm^2)O(2n⋅nm2). 不能接受。
我们发觉到题目中还有一个要求我们之前没有在意:bib_ibi 在滚榜时是不降的。这提示我们相邻两个 bbb 之间的差值总是非负的。我们把这个差值记为 did_idi,也就是我们对 bbb 数列进行了差分。
由于 bib_ibi 单调不降,如果我们认为这次选取 bib_ibi 之后,后一支队伍也先通过 bib_ibi 题。那么,他们还需要通过的题目就只剩下 did_idi 题了。
为什么考虑差分?我们来计算一下后一支队伍至少要通过多少题才能超过前一支队伍:
aj+bj=ai+bi+[i<j]∴bj=ai+bi+[i<j]−aj∴bi+di=ai+bi+[i<j]−aj∴di=ai−aj+[i<j]\begin{matrix}a_j+b_j&=&a_i+b_i+[i<j]\\\therefore b_j&=&a_i+b_i+[i<j]-a_j\\\therefore b_i+d_i&=&a_i+b_i+[i<j]-a_j\\\therefore d_i&=&a_i-a_j+[i<j]\end{matrix}aj+bj∴bj∴bi+di∴di====ai+bi+[i<j]ai+bi+[i<j]−ajai+bi+[i<j]−ajai−aj+[i<j]
我们很容易地发现,差分之后 bib_ibi 这一项就消掉了,取而代之的是差值 did_idi. 而差值 did_idi 可以由 iii 和 jjj 一步计算得到——我们就如此轻松地去掉了一维状态。
这时候回来看差分,也就不难理解了:因为这个 bbb 很难处理,而相邻的 bbb 之间又满足一维的递推关系,因此经过差分,我们可以消掉 bbb,从而达到砍掉一些状态的目的。
记 c=max{0,di×(n−∣S∣)}=max{0,(ai−aj+[i<j])×(n−∣S∣)}c=\max\{0,d_i\times (n-|S|)\}=\max\{0,(a_i-a_j+[i<j])\times (n-|S|)\}c=max{0,di×(n−∣S∣)}=max{0,(ai−aj+[i<j])×(n−∣S∣)},于是我们的转移方程化简为
f(S∪{k},k,j+c)←f(S,i,j)f(S\cup\{k\},k,j+c) \gets f(S,i,j)f(S∪{k},k,j+c)←f(S,i,j)
时间复杂度 O(2n⋅n2m)\mathcal{O}(2^n\cdot n^2m)O(2n⋅n2m). 经过常数优化后可以通过此题。
D2T3 - 支配
给定一张 nnn 个点 mmm 条边的有向图 GGG,其顶点从 111 到 nnn 编号。
对于任意两个点 u,vu, vu,v,若从顶点 111 出发到达顶点 vvv 的所有路径都需要经过顶点 uuu,则称顶点 uuu 支配顶点
vvv。特别地,每个顶点支配其自身。对于任意一个点 vvv,我们将图中支配顶点 vvv 的顶点集合称为 vvv 的受支配集 DvD_vDv。
现在有 qqq 次互相独立的询问,每次询问给出一条有向边,请你回答在图 GGG 中加入该条边后,有多少个顶点的受支配集发生了变化。
咕。
B卷D1T1 - 数对
给定 nnn 个正整数 aia_iai,请你求出有多少个数对 (i,j)(i, j)(i,j) 满足 1≤i≤n1 \le i \le n1≤i≤n,1≤j≤n1 \le j \le n1≤j≤n,i≠ji \ne ji=j 且 aia_iai 是 aja_jaj 的倍数。
统计每一个数的个数,然后枚举其倍数计算。
设 iii 有 cnt(i)cnt(i)cnt(i) 个,那么对于数 xxx:
同为 xxx 的数还有 cnt(x)−1cnt(x)-1cnt(x)−1 个,贡献 cnt(x)×(cnt(i)−1)cnt(x)\times (cnt(i)-1)cnt(x)×(cnt(i)−1).
kxkxkx 的数有 cnt(kx)cnt(kx)cnt(kx) 个,贡献 cnt(x)×cnt(kx)cnt(x)\times cnt(kx)cnt(x)×cnt(kx).
由于调和级数 h(n)=∑i=1n1i∼lnnh(n)=\sum\limits_{i=1}^{n}\frac{1}{i}\sim \ln nh(n)=i=1∑ni1∼lnn,所以总复杂度 O(nlogn)\mathcal{O}(n\log n)O(nlogn).
B卷D2T1 - 取模
给定 nnn 个正整数 aia_iai,请你在其中选出三个数 i,j,ki, j, ki,j,k(i≠ji \ne ji=j,i≠ki \ne ki=k,j≠kj \ne kj=k),使得 (ai+aj)modak(a_i + a_j) \bmod a_k(ai+aj)modak 的值最大。
考虑如何实现一个 O(n2log2n)\mathcal{O}(n^2\log_2 n)O(n2log2n) 的暴力:
枚举 kkk,即选取 aka_kak 作为模数。那么我们只需要在模 aka_kak 意义下,选取 ai+aja_i+a_jai+aj 最大的贡献答案即可。
这一步如果暴力枚举 i,ji,ji,j 那么每一轮复杂度会达到 O(n2)\mathcal{O}(n^2)O(n2),总复杂度是三方的。
记 aimodak=ba_i \mod{a_k}=baimodak=b,那么我们就是要枚举 bi+bj(modak)b_i+b_j \pmod{a_k}bi+bj(modak) 的最大值。
但是这个 bi+bjb_i+b_jbi+bj 的范围一定会在 [0,2ak)[0,2a_k)[0,2ak) 内,也就是要么在 [0,ak)[0,a_k)[0,ak) 内,要么在 [ak,2ak)[a_k,2a_k)[ak,2ak) 内。我们对这两段分开处理。
[0,ak)[0,a_k)[0,ak) 内。那么,我们对 bib_ibi 排序,使用双指针就可以轻松找到对于每一个 bib_ibi,使得 bi+bjb_i+b_jbi+bj 不会超过 aka_kak 的最大的 bjb_jbj. 直接用该值贡献答案,就是在 bi+bj<akb_i+b_j<a_kbi+bj<ak 时 bib_ibi 能贡献的最大答案了。
[ak,2ak)[a_k,2a_k)[ak,2ak) 内。那么,既然已经超过了 aka_kak ,也就是贡献必然为 bi+bj−akb_i+b_j-a_kbi+bj−ak,那么只需要令 bi+bjb_i+b_jbi+bj 最大即可。取 bib_ibi 最大的两项相加即可。
于是我们很轻松地将每一轮的复杂度压缩到了 O(nlogn)\mathcal{O}(n\log n)O(nlogn). 总复杂度看似是平方log的。
我们考虑答案的增长速度。
记当前答案为 ansansans. 不妨设 ai≤aj≤aka_i\le a_j\le a_kai≤aj≤ak,那么ai+aj≥2aia_i+a_j\ge 2a_iai+aj≥2ai.
又因为 (ai+aj)modak≤ans(a_i+a_j)\mod a_k\le ans(ai+aj)modak≤ans
所以 ai+aj−ak≤ansa_i+a_j-a_k\le ansai+aj−ak≤ans.
所以 2ai≤ak+ans2a_i\le a_k+ans2ai≤ak+ans.
所以 2(ai−ans)≤ak−ans2(a_i-ans)\le a_k-ans2(ai−ans)≤ak−ans.
因为 ai≤108a_i\le 10^8ai≤108 且不会随着答案增大而变化,而相邻两项 (ans−ai)(ans-a_i)(ans−ai) 是成倍的,因此 ansansans 至多存在 log 种取值。
所以如果按 aia_iai 从大到小,在每次取 aka_kak 前将 ak<ansa_k<ansak<ans 的情况剪枝,复杂度将不超过 O(nlognlogA)\mathcal{O}(n\log n\log A)O(nlognlogA). 可以获得满分。
Summary
技巧类
这次联选的题目我认为在技巧性上是非常强的,很注重一些小技巧的掌握和临场随机应变处理方式,这是赛后打题很难模拟出来的。下面会用加粗的字体标识一些关键词,剩下的大概都是一些心路历程或者纯属废话。
- (B卷 D2T1 取模)取模,gcd等数论运算中,常常会出现答案成倍递增/递减的情况,因此很多时候暴力的复杂度就是正确的。这个套路在数论中并不少见,但是在考场上要想得到,敢于写。
- (D1T2 矩阵游戏)构造是很朴素的,先构造一个解再去调整的想法也是很自然的,但是问题就出在这个差分约束上面。一般差分约束的题目会直接或者间接地给出两个数之间的关系,也就是必然是差,我们所要考虑的事情仅仅是建图与跑最短路。但是这题不一样,我们需要自己去构造差分约束的图模型。
而在构造的时候,我们用的构造方法很可能会产生不符合差分约束模型的东西,比如说限制条件变为“和”、做差不等号的方向出问题、构造的时候点/边的范围太大等等问题,这些都是我们要避免的。
在这道题目中,我们采用交错减法的方式,把奇偶项错开,使它始终一个加一个减,最后必定能构造出符合差分约束模型的边。这个解法看起来很简单,但是却需要对原有的构造思路进行调整,而不是在构造后的图上大分类讨论、打补丁之类的。我第一遍做这题的时候就犯了这个毛病,一度以为差分约束做不了这东西。
从这道题目中主要学习到的技巧是这个交错开的思路,这个对于其他的一些题目也有参考意义。另外,还让我注意到要学会在往下深挖和跳出来想之间的平衡 - (D2T2 滚榜)差分思想在dp上的运用。面对难以处理的一维,且相邻状态直接这一维存在一个递推关系,就直接用他们的差值把他们差分掉,从而优化掉这一维状态。这在差值 ddd 容易求出来的时候非常好用,能够以最轻松的方式降低时空复杂度。即使差值不能直接求出,有时候也能帮助优化一些状态的设计。
- (D2T1 宝石)使用并查集把询问合并起来,用来批量处理询问的转移,是一个实用的技巧。不仅很多时候比线段树好写,可能还跑的快,优点比较明显。
代码类
可撤销并查集的代码实现:
- 栈序撤销
- 存栈存足够的信息,不要漏,写一半回来补回很麻烦
- 存一个时间戳,回退的时候按时间戳回退,代码实现比较方便
并查集上对整个连通块打标记(且可能会被合并)的代码实现:
- 标记总是打在根上
- 合并的时候,讨论新的根是哪一个,重新打标记
- 存栈要存下原来的标记,撤销的时候记得还原标记
- (如果需要)合并到空并查集时,转移标记/重打标记要特别处理(小心漏掉这种情况!)
在实际编写代码的时候,我们还需要注意一些常数优化的技巧——以D2T2 滚榜为例。
- 枚举为 111 的二进制位。
在状态压缩时,我们会需要枚举 SSS 中为 111 的二进制位和 SSS 中为 000 的二进制位,才能保证是从 SSS 外新加入一个点到 SSS 内。
如果枚举的时候直接枚举每一位,判断其是否为 111 ,那么会有一点点的常数劣势。
而如果是每次取 lowbit 所在的那一位,那么就是精确地取到每一个 111 的位置。
虽然计算 lowbit 会花费一点时间,但是由于枚举时大部分情况是不满的,因此用 lowbit 去枚举确实会快上一点。 - 减少 long long 的使用。
这道题目中,最大的答案是会超出 int 范围的,但是 dp 数组f[][][]
的范围却不好估计。
因此,我们将一些最劣的情况跑一下,可以发现 f 数组的最大值不会超过 2×1072\times 10^72×107. 因此可以只开 int,而只在最后答案使用 long long 存储。
注:比赛中如果没有十足的把握请还是保险使用 long long 计算。 - 数组的排列顺序。
一般来说,我们会要求把小的维度放在前面,大的维度放在后面。因此,我的前两份代码都是按照f[i][j][S]
的顺序排列的(这里字母的意义同上)。
但是实际上,数组的访存速度是和访存的连续性相关的。
在这题中,SSS 必然是最先确定的,然后我们会枚举好 SSS 内外的两个转移点,最后就只要枚举 jjj 了。
所以,我们一定把 jjj 这一维放在最后,用两个指针指向*f[S][i]
和*f[S+{k}][k]
,这样转移的时候两个指针都会以极快的速度找到对应的内存。 - 循环展开
最后的枚举都是非常简单的连续操作,可以考虑循环展开。
单点运行时间在 0.105s 以内,和原来 T 飞好几个点形成了鲜明的对比。明明都是同一份代码呢,不就改了点顺序吗?
- 枚举为 111 的二进制位。
spfa 的实现技巧。
在判断负环的时候一般只会采用 spfa 和 floyd,而面对一些卡常题,我们还是需要学习 spfa 的一些优化技巧。- 过一个点超过 kkk 次即视为存在负环,kkk 为阈值,可以设的比较小保证效率。小概率会 WA.
- SLF. 如果当前 dis 比队首还小,那么塞进头部,否则塞进尾部。小概率会 T.
必须指出的是,这些算法都是错误的算法,前者正确性没有保证,后者复杂度没有保证(据称可以被卡到指数级别),除非真的卡不过去(且会丢掉很多分甚至与暴力同分),否则没有必要冒这个风险;相反,这些优化在没有刻意卡的时候效果显著,在没有办法的时候可以有效提高获得高分的可能性。
因此,尽管是错误算法,还是需要学习的。
联合省选 2021 解题报告相关推荐
- CSP-J 2021解题报告
放在前面:各位同学一定要好好复习初赛--不要像我一样初赛都没过-- 1.P7909 作为第一题--肯定不会很难(我还是卡了一下TAT) 可以想到输出的结果一定是 0~(n-1) 关键在于是判断输出n ...
- 远望智库未来产业研究院与资本实验室联合发布《2021全球区块链应用市场报告》...
来源:远望智库预见未来 从以比特币为代表的区块链1.0时代:到以智能合约为媒介,以金融应用为核心的区块链2.0时代:再到区块链应用于政务服务和更广泛的各行业,并开始推动信息互联网向价值互联网靠拢,短短 ...
- 【解题报告】2021牛客寒假算法基础集训营4
[解题报告]2021牛客寒假算法基础集训营4 前面的话 A :九峰与签到题 | 模拟 (签到题) B: 武辰延的字符串 | exKMP D :温澈滢的狗狗 | 二分 E: 九峰与子序列 | d p d ...
- 2021字节跳动校招秋招算法面试真题解题报告--leetcode19 删除链表的倒数第 n 个结点,内含7种语言答案
2021字节跳动校招秋招算法面试真题解题报告--leetcode19 删除链表的倒数第 n 个结点,内含7种语言答案 1.题目描述 给你一个链表,删除链表的倒数第 n 个结点,并且返回链表的头结点. ...
- NJU 2021 计算机拔尖(数学)测试 解题报告
NJU 2021 计算机拔尖(数学)测试 解题报告 试题链接, 万分感谢 Fiddie 大佬提供试题!!! 因为要准备 2022 计算机拔尖所以稍微写了一下,感觉难度很大. 1 题目 设自然数 n&g ...
- 湖南师范大学2021年4月1日愚人赛解题报告与标程
湖南师范大学2021年4月1日愚人赛解题报告与标程 A 题目描述 标程 B 题目描述 标程 C 题目描述 标程 D 题目描述 解法 标程 E 题目描述 解法 F 题目描述 解法 标程 G 题目描述 标 ...
- 2021字节跳动校招秋招算法面试真题解题报告--leetcode148 排序链表,内含7种语言答案
148.排序链表 1.题目描述 在 O(n log n) 时间复杂度和常数级空间复杂度下,对链表进行排序 2.解题报告 针对nlogn的排序算法,主要有快速排序,归并排序和堆排序.其中,堆排序利用了数 ...
- 解题报告(十八)数论题目泛做(Codeforces 难度:2000 ~ 3000 + )
整理的算法模板合集: ACM模板 点我看算法全家桶系列!!! 实际上是一个全新的精炼模板整合计划 繁凡出品的全新系列:解题报告系列 -- 超高质量算法题单,配套我写的超高质量的题解和代码,题目难度不一 ...
- 解题报告(十三)中国剩余定理(ACM / OI)
整理的算法模板合集: ACM模板 点我看算法全家桶系列!!! 实际上是一个全新的精炼模板整合计划 繁凡出品的全新系列:解题报告系列 -- 超高质量算法题单,配套我写的超高质量的题解和代码,题目难度不一 ...
最新文章
- 首场见习挑战赛倒计时3天!20000元奖学金瓜分就等你了!
- 跨年之际,中文版畅销书《TensorFlow深度学习实战大全》分享,直接送!
- 你朋友圈里的广告是怎么做到合你胃口的?
- error LNK2019: 无法解析的外部符号 _WinMain@16,该符号在函数 ___tmainCR... 2011年08月05日 09:08:15
- IOS Core Animation Advanced Techniques的学习笔记(五)
- 实验一 小凡和VMware虚拟机的使用练习
- Java处理split分割【for循环】
- Netty实战 IM即时通讯系统(三)Netty环境配置
- web安全----XSS漏洞之基本原理
- 常用的匹配正则表达式
- bootstrap 树形表格渲染慢_layUI之树状表格异步加载组件treetableAsync.js(基于treetable.js)...
- js自动篡改页面url地址 - 场景篇
- 浅谈.net事件机制
- python文件和路径操作
- fifo算法_前端进阶算法6:一看就懂的队列及配套算法题
- IDE工具的[多行光标编辑模式]
- mysql存储过程模糊查询_vb.net 使用存储过程进行模糊查询的教程
- 虚拟机 VMware Workstation 16 PRO 的网络配置
- LTP(Linux Test Project)使用指南
- 微信小程序 动态获取图片主色调作为背景
热门文章
- [芭比公主系列][国英双语]芭比之十二芭蕾舞公主 Barbie in The 12 Dancing Princesses
- 2D Pixel Perfect:使用Unity创建任天堂红白机风格复古游戏
- 哈工大2022年春季学期计算机系统大作业——程序人生
- ADB LOGCAT CMD
- web应用开发 -- 课堂作业 个人简介
- 一招教你学会如何用excel求重复项最大值
- 计算机科学与技术补中益气丸的成分,经典名方,补中益气丸运用解析
- 三十岁了学python还可以吗-三十岁了还可以学编程吗?只要你想,只要你做,什么时候都不晚...
- leetcode-腾讯精选50题-02
- python库源码分析_python第三方库Faker源码解读