HNOI2019做题笔记
代码比较长所以直接去LOJ看吧~
鱼(计算几何、向量)
比较套路的内容:枚举\(D\),对于其他所有点按照\(D\)极角排序,按照极角序枚举\(A\),这样垂直于\(AD\)的线也会以极角序旋转,可以使用双指针+map的方式维护合法的\(EF\)点对数量。
相对麻烦的是如何对于每个\(AD\)找到合法的\(BC\)的数量。注意到\(BC\)的限制条件很强,要求\(AD\)是\(BC\)的中垂线,且\(BC\)与直线\(AD\)的交点在线段\(AD\)上。
故预处理所有可能的\(BC\),设\(BC\)中点为\(E\),则计算三元组\((\overrightarrow{x},y,z)\),其中向量\(\overrightarrow{x}\)标识斜率,需要满足向量\(\overrightarrow{x}\)所在直线斜率等于\(BC\)所在直线的斜率、斜率相同直线的\(\overrightarrow{x}\)向量相同;\(y = \overrightarrow{x} ·\overrightarrow{OE}\);\(z= \overrightarrow{x} \times \overrightarrow{OE}\)。
其中\(y\)限制的是\(AD\)在\(BC\)中垂线上:对于任意向量\(\overrightarrow{a},\overrightarrow{b}.\overrightarrow{c}\),如果\(\overrightarrow{b}-\overrightarrow{c} \perp \overrightarrow{a}\),则\(\overrightarrow{a} ·\overrightarrow{b} = \overrightarrow{a} ·\overrightarrow{c}\),证明见点积的几何意义。所以如果\(AD\)为\(BC\)中垂线,那么\(\overrightarrow{OD} · \overrightarrow{x} = \overrightarrow{OE} · \overrightarrow{x}\)。而我们得到了\(\overrightarrow{AD}\)能比较轻松地得到\(\overrightarrow{x}\)和\(\overrightarrow{OD} · \overrightarrow{x}\),所以可以根据三元组前两维确定满足\(AD\)为\(BC\)中垂线的所有\(BC\)。
最后的问题是\(E\)在线段\(AD\)上。根据前面的步骤,\(E\)已经落到直线\(AD\)上,而当某个点\(P\)在直线\(AD\)上向着某一个方向移动时,\(\overrightarrow{x} \times \overrightarrow{OP}\)是单调的,所以如果\(E\)在线段\(AD\)上,则\(\overrightarrow{x} \times \overrightarrow{OE} \in (\overrightarrow{x} \times \overrightarrow{OA} , \overrightarrow{x} \times \overrightarrow{OD})\)。
所以可以预处理上述三元组并排序,对于一个\(AD\)就可以直接二分找到满足条件的\(BC\)的数量。总复杂度\(O(n^2logn)\),注意常数优化。
JOJO(KMP、主席树)
暴力就是把一次的插入一个一个插入KMP。这样插入显然太慢,所以考虑:将一次插入的一大块的字符看做一个新的字符(叫做字符段),连续插入\(i\)个\(b\)字符记做字符段\(b^i\)
我们在以新的字符段为基本元素的字符串上做KMP,KMP的next指向的串是当前前缀的一个border。比如\(a^2b^3a^2b^2a^3b^3\),它的next就是0 0 1 0 1 2。
注意到\(next[4]=0\),这是因为\(b^3 \neq b^2\),所以\(a^2b^3\)不能看做前四个字符段构成的字符串的一个border,这意味着因为\(b^2\)后面不可能插入\(b\),所以把\(b^2\)的next指向\(b^3\)的第二个\(b\)是没有意义的。当然这会使得有答案漏统计,所以要在暴力跳next的过程中统计答案;而\(next[5] = 1\),这是因为加入\(a^3\)时,虽然\(a^3 \neq a^2\),但\(a^2\)是第一个字符段,所以\(a^3\)的后两个\(a\)可以跟\(a^2\)匹配。这种情况也要额外算答案。
这样获得一个比较奇特的KMP。按照这种方法就可以获得第三个部分分。
对于第二个部分分,我们用自动机的形式魔改一下KMP:设\(trans[i][ch]\)表示在\(i\)号位置匹配\(ch\)会匹配到的位置。在第\(i\)个位置插入一个字符,就利用\(trans[i-1]\)找到它的next,\(trans[i] = trans[next_i]\),并将\(trans[i - 1]\)更新。
正解就是把第二个部分分和第三个部分分结合起来。注意到将这两个部分结合起来会出现以下的问题:
①字符集大小变为\(26 \times 10^4\),\(trans\)开数组难以接受,所以使用可持久化线段树动态开点维护\(trans\);
②\(trans\)一步到位但是没法统计答案,这个考虑:把总匹配长度的贡献分为两部分,假设\(b^1,b^2,...,b^l\)都被匹配,则一部分是\(\sum\limits_{i=1}^l match_i\),\(match_i\)表示当前插入的\(b^i\)在原串的\(next\)所在字符段之前的所有字符段的总长度,另一部分就是\(\sum\limits_{i=1}^l i\) 。
我们可以用主席树维护每个点每个字符的\(match_i\)。对于在位置\(p\)插入的一个字符段\(b^i\),假设当前插入\(b^j(j>i)\),且通过跳next跳到\(b^i\)前一个字符,那么\(b^1,b^2,b^3,...,b^i\)都会在这里匹配,即\(\forall x \in [1,i]\ match_x = p-1\),这就是一个区间赋值。而插入\(b^j\),会尝试匹配\(b^1,b^2,...,b^j\),所以需要求\(\sum\limits_{i=1}^j match_i\),也就是一个前缀求和。这样我们每一次插入的时候就可以快速求\(match\)并即时维护。求最长匹配长度\(l\)可以维护\(Max[x][i]\)表示从\(x\)匹配字符\(i\)最长可以匹配多少。
③询问要可持久化,其实这个可持久化可以直接离线,把插入的字符插成一个Trie,然后做带撤销的就可以。撤销就只需要把\(trans\)、\(match\)和\(Max\)还原即可。
多边形(模拟、组合)
第一问的答案下界是:\(n-3-\)不是多边形边界且端点均不是\(n\)的边的数量,达到的条件是:每一次都能够找到一个\((a,c)\)旋转,使得\(d=n\)。
可以使用归纳证明法证明一定可以达到答案下界,即对于任意多边形都一定存在一个旋转满足\(d=n\):
\(n=3\)时显然成立;\(n \geq 4\)时,若\(\exists i \in[2,n-2] , (i,n) \in e\),则\((i,n)\)一定不会被旋转,可认为这条边把当前多边形分割成两个小多边形,一个多边形为\(1,2,...,i,n\),另一个为\(i+1,i+2,...,n\),对于这两个多边形均能达到答案下界,所以当前局面能够达到答案下界;
如果不存在这样的点,意味着与\(n\)相连的点只有\(n-1\)与\(1\),所以有边\((1,n-1)\)。考虑多边形\(1,2,...,n-1\),对于每条边,它将多边形分割成两个多边形,保留其中同时存在顶点\(1\)与\(n-1\)的多边形和在其中的边,如是做最后一定会剩下三角形\(1,x,n-1\),而且\(x\)唯一,所以存在旋转\((1,n-1)\)使得旋转后有边\((x,n)\),回到上面的情况。所以当前多边形能够达到答案下界。
考虑第二问。注意到\((x,n)\)边,将原多边形分成了左右两个多边形,假设这两个多边形内经过一次旋转之后与\(n\)相连的点为\(y\)和\(z\),那么\(y\)和\(z\)连向\(n\)就会在\(x\)之后。不妨将\(y\)和\(z\)作为\(x\)的左右儿子,那么原来的操作序列就变为了一个森林,第二问就相当于找一个序列满足对于所有\(x\),\(fa_x\)在\(x\)的前面。
不难发现树上每个点的贡献是独立的,对于点\(x\),它对答案的贡献为\(\binom{sz_x - 1}{sz_{lson}}\),即它可以把它的两个后继的序列在不改变它们内部的顺序的情况下随意组合。整体的答案就是把所有点\(x\)的贡献乘起来再乘上把整个森林合并起来的方案数
考虑修改。如果修改的是一棵树的根,那么只有把森林合并起来的部分的贡献会有变化,否则这一次旋转对答案的影响就是某一个点的左儿子用\(Splay\)的一次\(rotate\)旋到父亲之上(注意这里一定是左儿子,如果选择的边对应某个点的右儿子,那么这条边在\((a,b,c,d)\)四边形中一定会对应\((b,d)\)边而不是\((a,c)\)边),贡献也只会有这些部分变化。把之前的贡献除掉、乘上新的贡献就可以了。
不懂的部分建议自行画图理解。
校园旅行(二分图、BFS)
一个比较显然的暴力:设\(f_{i,j}\)表示路径两端在\(i\)点和\(j\)点时是否存在回文路径,直接BFS维护转移,复杂度为\(O(\sum\limits_i \sum\limits_j d_id_j) = O(m^2)\)
看起来似乎没有更好的方法得到答案,所以考虑优化边的数量。
设\(ij\)边表示一端权值为\(i\),一端权值为\(j\)的边。将所有\(01\)边加入,原图会构成若干连通块,考虑仅保留每个连通块的一棵生成树。假设一条路径中,两端点中一端最短需要经过\(x\)次\(01\)边,另一端最短需要经过\(y\)次\(01\)边(\(x \equiv y \mod 2\),因为可以在\(01\)边上反复横跳),那么\(x\)和\(y\)的值可能会增加,增加数量可能不同。但是因为\(01\)边构成的连通块一定是一个二分图,所以在保留一棵生成树之后,一端一定会需要经过\(x + 2a(a\geq0)\)次\(01\)边,另一端需要经过\(y+2b(b\geq0)\)次\(01\)边,而加上\(2\)的倍数不会改变奇偶性,所以原来存在的路径在只保留一棵生成树之后仍然存在。
对于\(00\)边和\(11\)边我们也按照上述方法去做,但是注意到:如果某一个连通块内存在奇环,则可以通过绕奇环改变经过边数的奇偶性,但是生成树是一个二分图,所以如果某个连通块内出现了奇环,给这个连通块内的一个点加上自环,就和原连通块等价。
按照上述方式边数降低为\(O(n)\)级别,BFS复杂度变为\(O(n^2)\)。
白兔之舞(矩阵快速幂、任意模数NTT)
我们要求的就是\((E+Wx)^L\)在\(k\)进制循环卷积意义下的每一项的系数。
因为\(k \mid P-1\),所以\(w_k\)有意义。故先DFT求出点值表示,\(x=w_k^i\)之后就是一个矩阵快速幂,可以\(O(3^3klogL)\)地求出。
然后IDFT求出它的系数表示。IDFT式子是
\(f_i = \frac{1}{k}\sum\limits_{j=0}^{k-1}g_jw^{-ij}\)
注意到除了\(g\)以外其他都是整数,所以\(g_j\)可以是将\(w_k^j\)代入上式之后求得的点值表示矩阵,也可以直接取这个矩阵第\(X\)行第\(Y\)列的值。
然后可以多项式多点求值求\(w^{0},w^{-1},...,w^{-(k-1)}\)的值,但是因为要拆系数FFT所以常数肯定大到跑不过
考虑一个经典的拆\(ij\)的方式:\(ij = \frac{(i+j)^2}{2} - \frac{i^2}{2} - \frac{j^2}{2}\),但是\(i,j\)有可能是奇数,又无法确定\(w\)是否有二次剩余,所以换一种:\(-ij = \binom{j-i}{2} - \binom{j}{2} - \binom{i+1}{2}\),组合数都是整数,这样上式变为
\(f_i = \frac{1}{kw^\binom{i+1}{2}}\sum\limits_{j=0}^{k-1} \frac{g_j}{w^\binom{j}{2}} w^{\binom{j-i}{2}}\)
用拆系数FFT把后面的求和卷起来就可以求出\(f_i\)。
序列(贪心、可持久化单调栈)
这题结论很多,证明可以看官方题解或者感性理解,比较重要的结论有:
①最优方案一定是将原序列划分为若干等值块,每一个等值块的\(B\)相同,且一定是它们权值的平均数。
②贪心的正确性:用一个栈维护等值块,每一次新加入一个数,先把它自己看做一个等值块,然后不断考虑栈顶,如果栈顶的\(B\)比当前加入的等值块的\(B\)要大就把这两个块合并。
③把某个序列划分为左右两半,那么最优方案中,整个序列的划分点(划分点即满足\(i\)和\(i+1\)不在同一个等值块内的\(i\))一定是左右两半最优划分的划分点的并的子集。这意味着某个序列的最优划分可以看做将这个序列划分为两半,左边取最优划分,右边取最优划分,然后把左边的一段后缀和右边的一段前缀合并到一起,并且合并方式唯一。
④对于上面的一段后缀和一段前缀的合并,存在一种先二分右端点、二分右端点的过程中二分左端点进行check的单次\(O(log^2n)\)的做法
那么对于一次修改,可以认为把\(i\)单独看做一块,\(1\)到\(i-1\)看做一块,\(i+1\)到\(n\)看做一块,将\(i\)的权值修改之后三个块重新合并,还是可以用上面④的那种二分套二分的方法。至于如何求\(1\)到\(i-1\)和\(i+1\)到\(n\)的最优划分,直接用可持久化单调栈,预处理出所有前缀后缀的最优划分方案即可。复杂度\(O(nlogn+mlog^2n)\)
HNOI2019做题笔记的更多相关文章
- C语言程序设计做题笔记之C语言基础知识(下)
C 语言是一种功能强大.简洁的计算机语言,通过它可以编写程序,指挥计算机完成指定的任务.我们可以利用C语言创建程序(即一组指令),并让计算机依指令行 事.并且C是相当灵活的,用于执行计算机程序能完成的 ...
- C语言程序设计做题笔记之C语言基础知识(上)
C语言是一种功能强大.简洁的计算机语言,通过它可以编写程序,指挥计算机完成指定的任务.我们可以利用C语言创建程序(即一组指令),并让计算机依指令行事.并且C是相当灵活的,用于执行计算机程序能完成的几乎 ...
- SDOI2017 R1做题笔记
SDOI2017 R1做题笔记 梦想还是要有的,万一哪天就做完了呢? 也就是说现在还没做完. 哈哈哈我竟然做完了-2019.3.29 20:30
- SDOI2014 R1做题笔记
SDOI2014 R1做题笔记 经过很久很久的时间,shzr又做完了SDOI2014一轮的题目. 但是我不想写做题笔记(
- SDOI2016 R1做题笔记
SDOI2016 R1做题笔记 经过很久很久的时间,shzr终于做完了SDOI2016一轮的题目. 其实没想到竟然是2016年的题目先做完,因为14年的六个题很早就做了四个了,但是后两个有点开不动.. ...
- LCT做题笔记
最近几天打算认真复习LCT,毕竟以前只会板子.正好也可以学点新的用法,这里就用来写做题笔记吧.这个分类比较混乱,主要看感觉,不一定对: 维护森林的LCT 就是最普通,最一般那种的LCT啦.这类题目往往 ...
- java做题笔记
java做题笔记 1. 初始化过程是这样的: 1.首先,初始化父类中的静态成员变量和静态代码块,按照在程序中出现的顺序初始化: 2.然后,初始化子类中的静态成员变量和静态代码块,按照在程序中出现的顺序 ...
- SAM 做题笔记(各种技巧,持续更新,SA)
SAM 感性瞎扯. 这里是 SAM 做题笔记. 本来是在一篇随笔里面,然后 Latex 太多加载不过来就分成了两篇. 标 * 的是推荐一做的题目. trick 是我总结的技巧. I. P3804 [模 ...
- PKUWC/SC 做题笔记
去年不知道干了些啥,什么省选/营题都没做. 现在赶应该还来得及(?) 「PKUWC2018」Minimax Done 2019.12.04 9:38:55 线段树合并船新玩法??? \(O(n^2)\ ...
随机推荐
- adb server is out of date. killing... ADB server didn't ACK解决方法
在使用ADT Bundle进Android开发时,有时经常会碰到如下错误提示: adb server is out of date. killing... ADB server didn't ACK ...
- 微服务实战(二):使用API Gateway
微服务实战(一):微服务架构的优势与不足 微服务实战(二):使用API Gateway 微服务实战(三):深入微服务架构的进程间通信 微服务实战(四):服务发现的可行方案以及实践案例 微服务实践(五) ...
- ubuntu开发项目不能执行热更新
当项目开发到一定成熟度,项目基本上比较大(vue,angular,react,java,php等),在Ubuntu系统环境下,我们写了代码,但是不能想Windows一样执行热更新,这是因为Ubuntu ...
- jstat命令查看jvm的GC情况 (以Linux为例)
jstat命令可以查看堆内存各部分的使用量,以及加载类的数量.命令的格式如下: jstat [-命令选项] [vmid] [间隔时间/毫秒] [查询次数] 注意!!!:使用的jdk版本是jdk8. ...
- 杂牌机搞机之旅(一)——获得root权限(刷入magisk)
刷机不规范,抱机两行泪,谨慎刷机!! 一般获取root权限,我们都是通过软件来获取的,但是,软件破解root的成功率不是很高,现在,android版本普遍5.0+,大名鼎鼎的magisk可以直接获得r ...
- Linux设置Swap虚拟内存方法
linux可以文件或者分区来当作虚拟内存. 首先查看当前的内存和swap 空间大小(默认单位为k, -m 单位为M): free -m 查看swap信息,包括文件和分区的详细信息 swapon -s或 ...
- arcgis api 3.x for js 入门开发系列六地图分屏对比(附源码下载)
前言 关于本篇功能实现用到的 api 涉及类看不懂的,请参照 esri 官网的 arcgis api 3.x for js:esri 官网 api,里面详细的介绍 arcgis api 3.x 各个类 ...
- ADB和Fastboot最新版的谷歌官方下载链接
ADB和Fastboot for Windows https://dl.google.com/android/repository/platform-tools-latest-windows.zip ...
- TomCat的安装及测试
1.每个版本的安装都是一样,解压之后是一个文件夹 2.配置环境变量,右击我的电脑,属性--高级属性设置--环境变量--新建--配path即可(path后加;%CATALINA_HOME%\bin;) ...
- 从0开始的Python学习002python的数据类型
在创建变量的时候不用声明数据类型 # 创建变量 a = 10 # 打印变量值 print(a) 结果如下: 这种感觉和Java有很大的不同,感觉python很神奇,数据的类型是python自己决定的. ...