Virtual NOIP II Day 9 总结

Sweetlemon

2018-11-09 16:19:00

Personal

## 模板级题目测试 ### T1 单峰排列 看一下数据范围,$n\le 2\times 10^9$,显然是$\text{O}(1)$或者$\text{O}(\log n)$好题嘛! 仔细观察单峰排列的定义式,我们发现,其中有一个元素非常特殊:最大的元素一定在排列的“中间”,即它把排列分割成了单调增和单调减的两份。其余元素只要选定了是在这个元素的左边和右边,位置也就确定下来了;即最大元素左边的元素和右边的元素分别构成两个集合,这两个集合里面的元素的顺序已经确定。 因此,这题其实就是问,这两个集合有多少种选取方式。由于$[1,n-1]$中的每一个整数都可以选择是在左边还是在右边,因此选择方式有$2^{n-1}$种。 答案就是$2^{n-1} \mod{1234567}$。使用$\text{Kasumi}$计算即可。 ### T2 连接格点 一道最小生成树的简单题目。我们把给定的边连接起来,再建图跑一遍最小生成树即可。 建图有优化技巧。如果两个点已经相连,就无需加入这条边,这样可以减少排序的时间。另外,如果在建图前整个图就已经连通,就无需建图。 ### T3 [无序字母对](https://www.luogu.org/problemnew/show/P1341) 丢人,居然忘了欧拉路怎么写了。 请注意并不是对于所有的起点都有欧拉路,如果图中有两个奇点,欧拉路就只能以奇点为起点。另外,到达一个点后,也不是任意选一个相连的点走过去就一定能构成欧拉路,因此需要一定的回溯。 以字母为顶点,字母对为无向边。建图时统计每个点的度数,从字典序最小的奇点(如果有$2$个奇点)或偶点(如果没有奇点)开始$\text{dfs}$,将顶点存入栈中,经过一条边就把这条边删去;如果当前经过的边数达到$n-1$,就把栈中的顶点按照入栈顺序输出。 ## 模板题目测试 ### 线段树 写线段树的时候卡了半天……如果在考场上这样会不会死掉啊…… 注意两点: 1. $l$和$r$不要手抖写错! 2. 请注意`query`和`add`函数中,在递归调用`LC(root)`和`RC(root)`时,不要盲目更改范围。 这种写法是正确的: ```cpp void add(int root,int nl,int nr,ll v){ if (nl<=L(root) && nr>=R(root)){ SUM(root)+=(R(root)-L(root)+1)*v; ADD(root)+=v; return; } //这里使用逗号表达式是因为三元运算符两边的类型必须匹配 //但是我没法写出一个类型为void的常量 ADD(root)?(push_down(root),0):(0); if (nl<=MID(root)) add(LC(root),nl,nr,v); if (nr>MID(root)) add(RC(root),nl,nr,v); SUM(root)=SUM(LC(root))+SUM(RC(root)); } ``` 而这种写法是错误的: ```cpp void add(int root,int nl,int nr,ll v){ if (nl<=L(root) && nr>=R(root)){ SUM(root)+=(L(root)-R(root)+1)*v; //上面的L和R写反了 ADD(root)+=v; return; } ADD(root)?(push_down(root),0):(0); if (nl<=MID(root)) add(LC(root),nl,MID(root),v); if (nr>MID(root)) add(RC(root),MID(root)+1,nr,v); //上面两个递归调用可能扩大区间加的范围 //试仔细体会区别 SUM(root)=SUM(LC(root))+SUM(RC(root)); } ```