HNOI做题记录

算是……咕完了?

2013、2014的就咕了吧,年代太久远了,并且要做的题还有那么多……

LOJ #2112. 「HNOI2015」亚瑟王

发现打出的概率只和被经过几次有关。

于是\(dp_{i,j}\)表示第\(i\)张卡被考虑\(j\)次的概率,随便转移。

LOJ #2113. 「HNOI2015」接水果

脑子没了,这题都不会做??

考虑\((x,y)\in (a,b),lca(x,y)\ne x\)就等价于\(dfn_a\in [dfn_x,low_x],dfn_b\in [dfn_y,low_y]\),如果\(lca(x,y)=x\)就特殊处理一下,于是盘子变成矩形,水果变成点。

整体二分就没了。

LOJ #2114. 「HNOI2015」菜肴制作

经典套路,每次把能放最后的最大的往最后丢即可。

为什么?如果不是这样的话就把\(max\)后面的整体前移,\(max\)丢到最后,这样一定合法并且更优。

LOJ #2115. 「HNOI2015」落忆枫音

如果就只是给一个DAG的话就很好做,只需要每个点任意指定一个父亲即可。

考虑加了一条边\((x,y)\)(先把自环给判掉),那么强制这条边必选。如果除\(y\)以外的点仍然任意选父亲,那么就有可能出现环,要被删掉。考虑环除了\((x,y)\)边以外就只剩一个\(y\to x\)的链了,随便dp一下就可以了。

LOJ #2116. 「HNOI2015」开店

经典套路。

LOJ #2117. 「HNOI2015」实验比较

可以注意到缩点之后必然会是一个森林,我们再建一个虚点把根连过去,变成一棵树。

显然是设\(dp_{i,j}\)表示子树\(i\)分成\(j\)段的方案数,考虑如何合并子树。假设长度分别是\(i,j\),要合成\(k\),那么方案数就是\({k \choose i}\times {i\choose i+j-k}\),意思是先给\(i\)个分配位置,然后再选出一些合并两段的位置,剩下的位置就唯一确定了。

当然,我这种没智商的人是想不到这样做的,于是就只会预处理\(f_{i,j,k}\),但复杂度其实还是\(O(n^4)\)。

(但是上面那个这么像树形背包的东西恐怕是\(O(n^3)\)的?)

LOJ #2048. 「HNOI2016」最小公倍数

分块,写过了:https://www.cnblogs.com/p-b-p-b/p/10391114.html

LOJ #2049. 「HNOI2016」网络

一种暴力是插入路径的时候利用树剖把它插到其他点的堆里面,线段树维护,是\(O(n\log^3 n)\)的。

考虑二分答案,看经过这个点的路径数和所有的路径数是否相等。

如何求经过一个点、大于某个值的路径数?差分+树套树。

这样是时间\(O(q\log^3 n)\)、空间\(O(q\log^2 n)\)的,把树套树外层开成值域然后线段树上二分大概可以把时间变成\(O(q\log^2 n)\)的,然而空间还是炸了。

考虑整体二分,你发现不用树套树而是一个树状数组就做完了。

LOJ #2050. 「HNOI2016」树

考虑将每次copy过去的子树看作一个大节点,相邻两个大节点的距离是根之间的距离。

然后就只需要维护某个点对应回原树是哪个节点了。这个数据结构随便维护一下就没了。

据说代码很难写。你看我像是会写代码的鸽子吗?

LOJ #2051. 「HNOI2016」序列

口胡。

显然是考虑每个点作为最小值的贡献。先把区间最小值的贡献算出来。

考虑区间最小值右边那一段。设\(r_i\)表示右边第一个小于自己的位置,\(l_i\)同理。

如果\(r_i\le r\),那么\(i\)的贡献显然已经与\(r\)无关了。如果\(r_i>r\),那么还需要在原来的基础上减去\(a_i\times (i-l_i) \times (r_i-r)\),把最后一个括号拆开之后似乎也是很好求的。

于是主席树大概就可以了?

LOJ #2052. 「HNOI2016」矿区

咕了。

LOJ #2053. 「HNOI2016」大数

先把\(P=2,P=5\)的判掉,然后做一个\(a_i\times 10^{n-i}\)的后缀和,就变成了区间相同数的对数。

莫队直接上。

LOJ #2018. 「AHOI / HNOI2017」单旋

插入一个点的时候必然只会放在前驱的右儿子或后继的左儿子,取决于谁的深度更深,所以还是只需要维护深度。

单旋最小值\(x\)时可以发现除了右子树不变、\(x\)深度变为1以外深度全都+1,而只需要知道\(fa_x\)就可以知道右子树有哪些点。

发现维护父子关系很容易,于是做完了。

LOJ #2019. 「AHOI / HNOI2017」影魔

md没发现是排列懵了好久……

考虑到两个条件都要求某一个端点是最大值,我们在最大值处统计贡献。假设最大值在右边。

从左往右扫,设左边第一个大于\(k_i\)的是\(l\),那么\((l,i)\)中间的数如果是后缀最大值则贡献\(p_1\),否则贡献\(p_2\)。

强制所有都贡献\(p_2\),然后给后缀最大值的位置再加上\(p_1-p_2\)即可。由于后缀最大值就存在单调栈里,而它们反正都是要被弹掉的,所以复杂度正确。

离线或者直接主席树大概都可以。

LOJ #2020. 「AHOI / HNOI2017」礼物

FFT模板题?

LOJ #2021. 「AHOI / HNOI2017」大佬

首先可以很容易地用DP求出除回血外最多能做的操作次数是多少。

用某些方法发现怼大佬能搞出的伤害种类不多,于是BFS求出每种伤害需要的次数。

然后由于只能怼两次,乱搞就完了。

LOJ #2022. 「AHOI / HNOI2017」队长快跑

咕咕咕。

LOJ #2023. 「AHOI / HNOI2017」抛硬币

这里采用我这种菜鸡能想出来的暴力推式子方法。

考虑答案就是
\[
\sum_{i=0}^b \sum_{j=i+1}^a {b\choose i}{a\choose j}
\]
我们先把\(j>b\)的情况暴力算出来,后面只考虑\(j\le b\)的。

那么就有
\[
\begin{align*}
ans&=\sum_{i=0}^b \sum_{j=i+1}^b {b\choose i}{a\choose j}\\
&=2^b\times S-\sum_{i=0}^b \sum_{j=0}^i {b\choose i}{a\choose j}\\
&=2^b\times S-\sum_{j=0}^b \sum_{i=j}^b {b\choose b-i}{a\choose i-j}\\
&=2^b\times S-\sum_{j=0}^b {a+b\choose j}
\end{align*}
\]
其中\(S=\sum_{i=0}^b {a\choose i}=2^a-\sum_{i=b+1}^a {a\choose i}\)。

后面这个求和式可以利用组合数的对称性,变成\(2^{a+b-1}-\frac 1 2\sum_{j=b+1}^{a-1} {a+b\choose j})\),然后后面那东西再利用对称性把\(\frac 1 2\)消掉,只有\(2|(a+b)\)的时候会多出\(\frac 1 2{a+b\choose (a+b)/2}\),扩展lucas的时候操作一下就没了。当然你也可以直接发现它是\({a+b-1\choose (a+b)/2}\)。

LOJ #2494. 「AHOI / HNOI2018」寻宝游戏

不优美的解法

考虑从后往前推。

容易发现限制相当于是前面的要凑出一个数,长成\(0110??01?10\)的样子。

如果需要凑0但这一位是1,那么只能放and;如果需要凑1但这一位是0,那么只能放or;如果每一位都对上了,那么两个都可以放。限制也可以相应地往前推。

如果出现第三种情况,那么前面就会出现\(000??0?0\)或是\(11??1?1\)的限制,即0和1只有一种。

当01只有一种的时候,可以发现往前推的时候就只有一种情况需要递归算了。以\(00??0?\)为例。若当前数是\(001001\),那么如果放and,前面的限制就会是\(??????\),也就是任意,所以只需要算or的。

bitset优化,复杂度\(O(\frac{nmq}{\omega})\)。

优美的解法

努力观察性质,按每一位考虑,每个数变成0/1。

把这些数连成一串,第\(n\)位为最高位;把第\(n\)个数前面的符号作为最高位,and看做1。

然后你惊喜地发现,如果符号连成的数小于原来的数,就会获得一个1,否则0。

于是列出一堆不等式,你就赢了。

LOJ #2495. 「AHOI / HNOI2018」转盘

以下数组下标都从0开始。

首先通过打表/严谨证明,可以发现从一个点出发最多只会转一圈,否则不优。

为什么?如果当前时间大于等于出现的时间,那么将这个物品标记是不需要时间的。如果要转很多圈,那么显然每个物品都会是最后一圈的时候拿最优,所以前面的都是废的。

然后枚举从哪个点出发,断环为链,就可以贪心地算出最短时间。

优化?用各种方法可以发现,设\(b_i=T_i-i\),那么答案就是\(n-1+\max b_i\)。

现在是\(O(n^2q)\)的。我们把\(T\)倍长,仍然设\(b_i=T_i-i\),之后就可以发现答案就是\(n-1+\min_{i=0}^{n-1} \max_{j=i}^{2n-1} b_j\)。可以做到\(O(nq)\)。

然后咋做?该上数据结构了?

考虑楼房重建的套路,设\(solve(l,r,mx)\)表示后面的最值是\(mx\),只考虑\([l,r]\)的答案。如果\(mx\ge max(mid+1,r)\),那么右儿子的答案定下来了,查左儿子;否则,左儿子的答案与\(mx\)无关,可以之前记下来,查右儿子。

然后就做完了。

LOJ #2496. 「AHOI / HNOI2018」毒瘤

很容易想到容斥:强制一些非树边不合法,相当于强制某些点必选,然后求树的方案数。

我会动态DP!

动态DP不够优美,考虑别的方法。

由于总共涉及到的点只有22个,我们把虚树建出来,在上面DP。

虚树上DP独立集个数的时候当然不能按普通树的做法写,而是要预处理出儿子对父亲的贡献系数,即\(k_0 dp_{son,0}+k_1 dp_{son,1}\)。

然后就做完了。令\(s=m-n\),复杂度\(O(s2^{s}+sn)\)。

如果用动态DP的话要注意一些细节,比如矩阵内要维护0的个数(这样才能撤销)、枚举集合不能循环枚举而是要dfs枚举,使得相邻两个集合只有一个位置需要修改。还记得格雷码吗?

LOJ #2508. 「AHOI / HNOI2018」游戏

把没有门的点缩在一起。

如果\(i\to i+1\)的钥匙比\(i\)大,那么显然是不能往前走了,连边\(i\to i+1\)。

如果\(i-1\to i\)的钥匙比\(i\)小,那么同理,连边\(i\to i-1\)。

于是搞出一个图,\(x\to y\)表示只能\(x\)转移到\(y\)。

然后暴力按拓扑序转移即可。复杂度不知为啥是对的。

LOJ #2509. 「AHOI / HNOI2018」排列

题目给的条件真是太让人难以理解了……

经过仔细分析,可以发现其实就是要求一个排列\(p\),使得在\(p\)里面\(a_i\)比\(i\)早出现,并且要求\(\sum i\times w_{p_i}\)最大。

可以理解为\([1,n]\)的数都带上了一个权值,要把他们放成一排,并且越靠后的系数越大。

我们连边\(a_i\to i\),显然构成了一棵以\(0\)为根的树,父亲比儿子早出现。

容易发现当权值最小的数可以放在最前面的时候,一定会把它放在最前面,这样显然最优。于是根据树上贪心的套路,可以把它和父亲缩在一起。

但缩在一起的连通块其实是一个序列,此时怎么比较呢?

设他们分别是\(a[1...n],b[1...m]\),那么可以列出式子:

\[
S_1=\sum_{i=1}^n iw_{a_i}+\sum_{i=1}^m (i+n)w_{b_i}\\
S_2=\sum_{i=1}^n (i+m)w_{a_i}+\sum_{i=1}^m iw_{b_i}\\
S_1-S_2=n\sum_{i=1}^m w_{b_i}-m\sum_{i=1}^n iw_{a_i}\\
S_1>S_2\Longleftrightarrow \frac 1 m\sum_{i=1}^m w_{b_i}>\frac 1 n\sum_{i=1}^n w_{a_i}
\]

(有可能系数不从1开始,但是无所谓)

所以只需要比较平均值即可。

LOJ #2510. 「AHOI / HNOI2018」道路

md,D2T3放这种题??(还是说loj编号有点锅?)

结果我tm还没做出来????

设\(dp_{x,p,q}\)表示\(x\)到根的公路和铁路有几个被修了,随便转移。

LOJ #3054. 「HNOI2019」鱼

D1T1计算几何??咕了!

LOJ #3055. 「HNOI2019」JOJO

先考虑没有可持久化怎么做。

对于每一段,我们只需要存最后那个位置的\(nxt\)指向哪里。

然而,如果它指向了一个段的中间,我们不可能在它后面加东西,所以这对后面加字符是无效的,而且我们也不想存每一段中间的\(nxt\)。

所以我们再引进一个指针\(link\),指向最长的\(border\),使得这个\(border\)最后一段是完整的。简而言之\(link\)就是把aaaa看做一个字符a4,然后求\(nxt\)。

此时,没有可持久化的部分就做完了:每次暴力跳\(link\)找到这一段每一部分的\(nxt\),然后继续跳\(link\)直到找到这一段结尾的\(link\)。

要可持久化呢?按照可持久化kmp的套路,我们需要记一个\(trans_{c,x}\)表示在后面加一段长度为\(x\)的\(c\)字符,它的结尾的\(nxt\)会指向哪里。\(link\)同理。

此时就不能暴力往前跳了,而是查最后一段的\(trans\),直接跳过去,然后继承那里的\(trans\)。

不同点在哪呢?你需要统计答案,而且这一段里面每一个字符的\(nxt\)不一定是相同的,所以你要查询前缀和;跳过去之后它后面那一段可能是长度为\(y\)的\(d\)字符,此时\(trans_{d,l}(l\in [1,d])\)都要指向那一段,所以要前缀赋值。

最后你可能还需要开一个主席树维护当前情况每一个位置属于哪一段,就做完了。

代码是不可能写的

LOJ #3056. 「HNOI2019」多边形

手玩可以发现终止状态是所有边都连向\(n\),而最小步数就是没有连向\(n\)的边的个数。

考虑如何求方案数。你发现\(n\)连出去的边会把圆割成若干段,可以把它们视为森林。

然后你发现一段可以被看成是一棵二叉树,于是做完了。

带修改也问题不大,因为只会影响到几个点而已。

LOJ #3057. 「HNOI2019」校园旅行

日常不会做智商题……

暴力很好想:把所有初始合法的点对丢进队列,然后暴力枚举两边往同色点走,\(O(m^2)\)。

然后优化边数??

考虑同色连通块的作用,可以两边在里面乱跳,那么能否跳到同一个点呢?容易发现这和距离奇偶性和是否有奇环有关。对于二分图,只需要留一棵生成树,否则再加一个自环。

异色连通块也差不多,但它肯定是二分图,所以留一棵生成树。

然后边数就变成\(2n\)就可以暴力了。

LOJ #3058. 「HNOI2019」白兔之舞

https://www.cnblogs.com/p-b-p-b/p/11964304.html

我觉得这题比T1可做……

LOJ #3059. 「HNOI2019」序列

寻找性质,发现当\(A_i\ge A_{i+1}\)的时候,必然有\(B_i=B_{i+1}\)。

而且,你发现把\(A_{i},A_{i+1}\)都赋成平均数之后仍然会保证\(B_i=B_{i+1}\),并且答案只会差一个常数,所以可以直接变成平均数。

然后推广一下,一个单调不升子段也是可以直接合并到一起去的。

于是暴力做法就出来了:从左往右维护一个单调栈,每次加一个数的时候就拼命往前合并,直到满足单调不降。当然从右往左合并也是可以的。最后选的\(B_i\)就是单调栈里的数。

如何支持多组询问呢?

大胆猜想可以先把左右的单调栈求出来,然后再合并两个单调栈,答案仍然是对的。不会证明,但很多题都是这么做的。

如何合并两个单调栈?考虑插入一个元素的时候可以二分要合并多少个元素做到\(O(\log n)\),而我们可以先二分右边有多少个要被合并到左边去,然后在左边二分得到真正被合并出了什么,再在右边看是否满足单调不降。

容易发现这是有单调性的。配合线段树上二分就可以做到\(O(n\log^2 n)\)。

两边的单调栈可以预处理出来,左边一边做一边加、右边先做完然后再回退。

我觉得这题比T1T2都可做……HNOI什么毒瘤题目顺序……

上一篇:Windows消息【一】 消息队列


下一篇:org.apache.jasper.JasperException