『HNOI2020 考试心得』

Day1

T1 icefire
题意简述

​ 一场比赛即将开始。每位战士有两个属性:温度和能量,有两派战士:冰系战士要求场地温度不低于他的自身温度;火系战士要求场地温度不高于他的自身温度。当场地温度确定时,双方能够参赛的战士以任意方式分别排成一队。每回合双方各出一个战士,两位战士消耗相同的能量,能量少的战士耗尽能量退出比赛,能量有剩余的战士将继续战斗。如此循环,直至某方战士队列为空,比赛结束。

​ 你需要寻找可能的最高温度,使冰火双方消耗总能量之和最高。

​ 目前比赛还处于报名阶段,且还没有任何战士报名,接下来你将不断地收到报名信息和撤回信息。其中,报名信息包含报名战士的派系和两个属性,撤回信息包含要撤回的报名信息的序号。每当报名情况发生变化时,你需要立即报出当前局面下的最佳场地温度,以及该场地温度下双方消耗的总能量之和是多少。若当前局面下无论何种温度都无法开展比赛(某一方没有战士能参赛),则只要输出 Peace

​ 信息的数量 \(Q\leq 2\times 10^6\) ,温度 \(x\leq 2\times 10^9\) ,能量 \(\sum y\leq 2\times 10^9\)

考时分析

​ 带撤销操作,于是考虑用线段树分治来做。问题转化为到达叶子节点后如何快速判定最佳场地温度。显然,对于一个确定的温度,消耗的总能量为 \(\min(a,b)\times 2\)\(a\) 为符合条件的冰系战士的总能量,\(b\) 为符合条件的火系战士的总能量。总能量是连续的一段,可以用树状数组来维护。由上可以还发现答案为温度的函数,设其为 \(F(T)\) ,那么 \(F\) 是单峰函数,可以三分。于是我就想到了 \(\mathcal{O}(Q\log^{2}Q)\) 的做法(60pts)。尝试优化至一个 log 但失败了。

正解做法

​ 首先考虑另外一种 \(\mathcal{O}(Q\log^{2}Q)\) 的做法:二分温度,在树状数组上查询符合的冰系总能量和火系总能量,再根据这个调整答案和调整 Mid 值。其实,线段树本身就有二分的性质,可以考虑先离线下来,然后把 \(x\) 离散化,用 \(x\) 来建一棵线段树。树上节点记录区间内的冰系能量总和和火系能量总和,以及自己右儿子一直往左走到达的叶子结点的温度。修改时单点修改,查询时,每个点记录的温度是刚好可以达到左儿子的子树内的冰系总和和右儿子子树内的火系总和的最小值两倍的最大温度。如果左边的冰系总和大就往左走,右边的火系总和大就往右走就行了。本质上,这个优化就是空间换时间,用线段树的4倍空间来避免每次二分都要在树状数组上查询。

总结

​ 带撤销操作的题目还是不要只是想线段树分治...把思路搞僵化了不太妙。其实在线段树分治上的每个叶子结点进行的统计答案的操作,在原序列上是可以暴力在线进行更新的。所以如果想到了线段树分治的做法,不妨再重新把统计答案的操作带回原序列内看看是否可行,然后这样或许就可以想到一些复杂度更优秀的做法。

重点:线段树分治主要解决的问题是撤销操作实现比较困难或者某些加入操作比较困难的数据结构。 比如:动态维护线性基、并查集。线性基主要是不好撤销,并查集主要是当加入的边构成环时不好再加入。而像树状数组这样插入删除都方便的数据结构,是没有必要用线段树分治来维护的。

T2 problem
题意简述

​ 给定 \(n,x,p\) 和多项式 \(F(x)=a_0+a_1x+...+a_mx^{m}\) ,求 \((\sum_{k=0}^{n}F(k)\times x^k\times(_{k}^{n})) \mod p\) 的值 \((n\leq 10^9,m\leq 10^3)\)

考时分析

​ 首先打掉了纯暴力的30pts。然后幸亏之前专门看过知乎的组合数一个专辑,于是在考场上想到了用二项式定理来做 \(m=0\) 的部分分。于是就40pts交了。

正解做法

​ 第二类斯特林数: \(S(n,m)\) 表示 \(n\)不同的小球放在 \(m\)相同的盒子里无空盒的方案数。由组合八题可得: \(S(n,m)=S(n-1,m-1)+S(n-1,m)\times m\)\(S(n,m)\) 也可以用 \(\{^{n}_{m}\}\) 来表示。

第二类斯特林数有个优美的性质:\(m^n=\sum_{i=0}^m C(m,i)\times S(n,i)\times i!\)考虑组合意义:左边是 \(n\) 个不同的小球放在 \(m\) 个不同的盒子里允许空盒的方案数,右边是枚举非空盒的数量 \(i\) ,然后因为第二类斯特林数计算的是相同的盒子,所以还要乘上 \(i!\)

​ 下降幂单项式:\(k^{\underline{m}}=\prod_{i=k-m+1}^{k}i\)\(k^{\underline{0}}=1\)

下降幂单项式也有个优美的的性质:\(C(n,k)\times k^{\underline{m}}=C(n-m,k-m)\times n^{\underline{m}}\)

​ 于是我们有两个方法做这道题目。首先考虑下降幂的做法。

​ 设 \(F(k)=G(k)=\sum\limits_{i=0}^{m}b_i\times k^{\underline{i}}\)

​ 将 \(G(k)\) 带入原式,得原式\(=(\sum\limits_{k=0}^{n}G(k)\times x^k\times(_{k}^{n})) \mod p\)

\(=(\sum\limits_{k=0}^{n}x^k\times\sum\limits_{i=0}^{m}b_i\times k^{\underline{i}}\times (_{k}^{n})) \mod p\)

\(=(\sum\limits_{k=0}^{n}x^k\sum\limits_{i=0}^{m}b_i\times (_{k-i}^{n-i})\times n^{\underline{i}}) \mod p\)

\(=(\sum\limits_{i=0}^{m}b_i\times n^{\underline{i}}\sum\limits_{k=0}^{n}x^k\times (_{k-i}^{n-i})) \mod p\)

​ 考虑组合数的组合意义:当 \(k-i\leq0\) 时,右边等于 \(0\) 。所以内层可以枚举 \(k-i\) 来缩小范围。

​ 故原式 \(=(\sum\limits_{i=0}^{m}b_i\times n^{\underline{i}}\times x^i\sum\limits_{k=0}^{n-i}x^k\times (_{\ \ i}^{n-i})) \mod p\)

\(=(\sum\limits_{i=0}^{m}b_i\times n^{\underline{i}}\times x^i\times (x+1)^{n-i}) \mod p\)

​ 也就是说,如果我们可以求出 \(b_i\),那么就可以 \(\mathcal{O}(m)\) 求出答案。怎么求出 \(b_i\) 呢?

\(F(k)=\sum\limits_{i=0}^{m}a_ik^i=\sum\limits_{i=0}^{m}a_i\sum\limits_{j=0}^{k}S(i,j)\times k^{\underline{j}}\)

​ 考虑第二类斯特林数的组合意义:当 \(i>j\) 时, \(S(i,j)=0\) 。所以 \(j\) 的上限可以是 \([i,+\infin]\) (反正都等于 \(0\))。由于我们需要的下降幂在里面,我们自然希望可以将它移出来,所以我们将 \(j\) 的上限设定为 \(m\) 。于是得到:

\(F(k)=\sum\limits_{j=0}^{m}k^{\underline{j}}\times(\sum\limits_{i=0}^{m}a_i\times S(i,j))\)

​ 后面那一堆自然就是 \(b_i\) 啦。通过这个式子,我们可以用 \(\mathcal{O}(m^2)\) 来求解斯特林数和 \(b_i\) ,于是这个题就做完啦。收获颇丰。这里特别留意一下第二类斯特林数的求法。

s[0][0]=s[1][1]=1;
for(int i=1;i<=m;++i,s[i][1]=1)
for(int j=2;j<=i;++j)
	s[i][j]=s[i-1][j-1]+s[i-1][j]*j;
总结

​ 第二类斯特林数以前见过,觉得太难了就没坚持搞。你要时刻记住你是一个准高二了啊!一个本应就知识储量而言应是全国赛水平的高二啊!再也不能抱着“高山仰止,景行景止”的态度来搞竞赛了,而应像凯撒那样“我来,我看见,我胜利”。只要是看见的算法,只要是别人做出来的算法,都应对自己提出更高的要求。

​ 尤其是数学方面,莫比乌斯反演、斯特林反演、组合数反演、分治NTT、多项式求逆等等这些超级高深的知识,我感觉又是一道类似于树链剖分、平衡树、主席树这样的无垠平台。在平台之下,自然觉得暗无天日;但在平台之上,却是另一番锦绣前程。必须抽出时间来把数学知识逐个击破。

T3 shop
题意简述
考时分析

​ 看到又是极大子集又是异或又好像要DP就觉得不可做。

正解做法
总结

Day2

T1 transfer
题意简述

​ 一条道路上排列着 \(m\) 个相隔 \(1\) 个单位长度的信号站,初始时从左至右依次编号为 \(1,2,…,m\) 。相邻信号站之间信息传递需要消耗 \(1\) 单位时间。道路的最左侧有一个控制塔,与最左侧信号塔相隔 \(1\) 单位长度。控制塔能与任意信号站进行双向信号传递,但每单位长度距离需要消耗 \(k\) 个单位时间。对于给定的长度为 \(n\) 的信号传递序列 \(S\),传递规则如下:

  • \(n-1\) 次信号传递,第 \(i\) 次信号传递将把信号从 \(S_i\) 号信号站传递给 \(S_{i+1}\) 号。
  • \(S_{i+1}\) 号信号站在 \(S_i\) 号右侧,则将信号从 \(S_i\) 号直接传递给 \(S_{i+1}\) 号。
  • \(S_{i+1}\) 号信号站在 \(S_i\) 号左侧,则将信号从 \(Si\) 号传递给控制塔,再由控制塔传递给 \(S_{i+1}\) 号。

​ 阿基作为大工程师,能够任意次交换任意两个信号站的位置。这样会使得 \(S\) 消耗的传递时间改变,现在请你最小化 \(S\) 的传递时间 \((m\leq24,n\leq10^5)\)

考时分析

​ 考试的时候就思考如何状压DP来做。进一步地,如果枚举的集合 \(s\) 中存在 \(x\),那么对于 \(\forall y \notin\ s\),都应该利用未来费用处理。如何设置未来费用呢?可以先将 \(S\) 处理出pre[i][j](表示 \(S\)\(i\) 恰位于 \(j\) 前一个的个数)和suf[i][j](表示 \(S\)\(i\) 恰位于 \(j\) 后一个的个数)。然后大概就可以转移了。但是我预估了一下时间复杂度为 \(\mathcal{O}(2^m\times m^3)\) ,连 60pts 的暴力都不行,再考虑到实现较为复杂,就放弃了。 只写了个 30pts 的暴力。

正解做法

​ 正解的确是状压DP,但比起我的更巧妙。首先,设 \(x\) 所处的位置为 \(p_x\),那么对于一次 \(x\rightarrow y\) 的信号传递,花费为:

\[\begin{cases} p_y-p_x\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ (p_xp_y) \end{cases} \]

​ 预处理 \(e_{x,y}\) 表示 \(x\rightarrow y\) 的传递次数。令 \(S\) 为已经确定了位置的信号站的集合,那么有:

\[f_{S}=\min\limits_{x\in S}(f_{S-x}+|S|\times\sum\limits_{y\in S-x}(k\times e_{x,y}+e_{y,x})+|S|\times\sum\limits_{y\notin S}(k\times e_{y,x}-e_{x,y})) \]

​ 此时的 \(|S|\) ,其实就是 \(p_x\) 。注意到这一点后,这个转移显得非常巧妙了。首先是对于 \(k\times(p_x+p_y)\) 的计算——在轮到 \(x\) 的时候,我们已经确定了 \(p_x\),那么就可以计算任意在 \(x\) 之前的信号站 \(y\)\(e_{x,y}\) 的一半贡献,和在 \(x\) 之后的点 \(y\)\(e_{y,x}\) 的一半贡献;对于 \(p_y-p_x\) 的计算,在枚举到 \(x\) 时,我们可以假设在 \(x\) 之前的某一个 \(y\) 恰是第 \(0\) 个,那么此时的贡献就是 \(p_x\times e_{y,x}\) ;在之后枚举到 \(y\) ,确定 \(p_y\) 时,就要把贡献减去 \(p_y\times e_{y,x}\)差分计算贡献和分条件计算贡献两种思想是处理费用问题的利器

​ 但这时算法的复杂度是 \(\mathcal{O}(m^2\times2^m)\) 的,需要进一步优化。在 \(\min\) 内,我们多一个 \(m\) 枚举的是 \(|S|\) 的系数。我们假设 \(K_{S,x}\) 表示 \(f_{S-x}\rightarrow f_{S}\)\(|S|\) 的系数,这是一个无关于 \(f\) 的定值,并且是可以递推的:

\[K_{S,x}=K_{S-y,x}+(k+1)\times e_{x,y}+(1-k)\times e_{y,x} \]

​ 那么我们可以 \(\mathcal{O}(m\times 2^m)\) 预处理+转移。时间上是可以通过本题了。

​ 但是空间复杂度太大了不能通过这个题。并且我太菜了还不会优化空间。

总结

​ 写DP的转移方程时,不要总是想着什么都暴力枚举,写出方程就万事大吉。本题的方程若是 \(\mathcal{O}(m^3\times2^m)\) 来实现思维上确实没有什么障碍,但是无法通过本题。既然是要通过练习DP来训练思维的话,就要学会如何用更少的枚举次数实现更多的功能。尤其是本题分条件的计算贡献太巧妙了

T2 tree
题意简述

​ 给定一棵 \(n\) 个结点的有根树 \(T\),结点从 \(1\) 开始编号,根结点为 \(1\) 号结点,每个结点有一个正整数权值 \(v_i\)

​ 设 \(x\) 号结点的子树内(包含 \(x\) 自身)的所有结点编号为 \(c_1,c_2,\cdots,c_k\)\(d(x,y)\) 表示树上 \(x\) 号结点与 \(y\) 号结点间简单路径长度,定义 \(x\) 的价值为:

\(val(x)=(v_{c_1}+d(x,c_1))\oplus(v_{v_2}+d(x,c_2))\oplus \cdots\oplus(v_{c_k}+d(x,c_k))\)

​ 请你求出 \(\sum\limits_{i=1}^n val(i)\) 的结果 \((n\leq5\times 10^5)\)

考时分析

​ 碰到异或的题目就犯傻。拿到手开始回忆曾经做过的有关异或的题目,想到了二进制拆分逐位解决、两等数异或为零等等性质,希望找到突破口,但最终还是失败了。甚至连链上的部分分也不会做,只能打 10pts 暴力草草结尾。

正解做法

​ 假设我们现在是用一个数据结构来维护子节点的值,那么我们需要支持以下操作:

  • 插入

  • 合并

  • 全局加一

  • 求全局异或和

​ 由于是要维护全局加一和全局异或和,所以考虑用 0/1trie 来解决(这个套路要记得)。其中,由于有全局加一的操作,所以我们考虑从低到高建立 trie 树。设左儿子为 \(0\) ,右儿子为 \(1\) ,这样全局加一就是交换左右儿子,并递归处理左儿子。

​ 好像这样子就差不多了?合并类似于线段树合并,求全局异或和类似于树形DP。

总结

​ 以后做数据结构题,分两步走。第一,在纸上罗列出数据结构需要支持的操作;第二,分析有无操作是可以拆分成若干个更为简单维护的操作来实现 。这两步大概是数据结构题的精髓吧。同时,对于这个题 0/1trie 树的套路,也要通过做题来巩固。

T3 count
题意简述

​ 给定一个 \(n\) 个点 \(m\) 条边的无向图 \(G\) ,保证图中无重边和自环。每一条边有权值 \(w_i\) 。 对于 \(G\) 的一个生成树 \(T\),定义 \(T\) 的价值为:

\(v(T)=(\sum\limits_{i=1}^{n-1}w_{e_i})+\gcd(w_{e_1}+w_{e_2}\cdots+w_{e_{n-1}})\)

​ 请求出 \(G\) 的所有生成树 \(T\)\(998244353\) 取模的值 \((n\leq 30,m\leq\frac{n(n-1)}{2})\)

考时分析

​ 首先如果图是基环树,可以先任意生成一棵树,再暴力删边。加上这档分和纯暴力共 30pts 。对于 \(w_i\) 均相同的数据点,由于 \(v(T)\) 相同,于是只要求出图的生成树个数就可以了。但这时要用到矩阵树定理,然后我不会。对于 \(w_i\) 均为质数的数据点,后面的 \(\gcd\) 要么是 \(1\),要么是 \(w_i\)。如果只考虑 \(1\), 显然可以先暴力删边再用矩阵树算出剩余的生成树的个数来计算这条边为答案做出的贡献;然后再考虑 \(\gcd\)\(w_i\) 的情况。然后我就发现我如果会矩阵树定理可以再多得 40 pts,但我不会。“问君能有几多愁,恰似一江春水向东流。”

正解做法
总结

考试经验与反思

  • Day1 花费了比较长的时间来调 T1 的三分,后来才发现当导函数不严格单增或单降时是无法三分的。最后还是找到了解决方法,但痛失写 T3 暴力的时间。学过的知识自以为懂了,其实不牢固。
  • Day1 拿到 T1 直接套线段树分治,自以为是正解。但实际考完后经过分析才发现对于树状数组、线段树之类的数据结构是没必要用线段树分治来维护的。思维搞僵化了,不能融汇贯通。经过这次省选,对于几类数据结构(树状数组、平衡树、线段树、主席树、并查集等算一类;整体二分、线段树分治、树套树等算一类;LCT 等动态树算一类),我感觉它们之间是由某些联系的,在维护对象、适用范围上也是有很多技巧的。希望自己能抽时间整理。
  • Day2 太多的部分分都是听说过而不会做,说明平时落实的不到位。省选时对于某个知识点,要么不考,要考就会考得比较深(矩阵树可能算比较深的了吧)。只有对诸多算法都了如指掌,才能在应对部分分的时候得心应手。不愿从舒适的阴沟里站起来去接受烈日,就看不到更广阔的蓝天。畏难是我求知路上的拦路虎。
  • 太多的题目涉及“异或”操作。找时间整理自己做过的与“异或”有关的题目的总结吧。尤其应明确分清楚在特定题目内用特定的性质/特定的数据结构来维护的原因。

你可能感兴趣的:(『HNOI2020 考试心得』)