2022.03.29省选模拟赛总结
1.时间安排
7:30~8:00 看题面
T1:数据结构题,感觉十分可做,可能是可持久化trie?
T2:期望题,有10分搜索,n,m,q=15也许能状压。
T3:字符串题,对每个子串都要统计,好像很复杂,放最后做。
8:00~9:00 T1
写完第一档暴力状压枚举后,发现选出若干数不能用可持久化trie做,想了一下发现用线性基就能维护了,就预处理出来200*200的线性基过掉第二档。
因为线性基不支持撤销(也可能支持,但考场上不会写),就想到了回滚莫队,写了一份,过掉第三档。
9:00~9:20 T2
写完搜索。思考状压,没有思路,特殊分段也没想法,本来也不擅长期望题,直接跳。
9:20~10:00
一开始想的很复杂,把所有子串全列出来建个树,再dfs搜,想着太麻烦了不想写,突然一想并不需要真的建出来树跑,直接枚举左端点做n遍kmp求两遍前缀和就好了,写完大样例能过,因为想赶紧写T1就没再管了。
10:00~11:00
数据范围实在太大,而且线性基不得不带一个log,所以想有没有两个log的做法。
突然想到对于一个左端点最多只有log个本质不同的线性基,就大胆势能分析一波,线段树维护线性基,离线后扫描线做,复杂度是两个log,但是很遗憾写挂了,一直调了半个多小时都没结果,放弃这个思路。
11:00~12:00
不得不写三个log的做法,能得一点是一点,写了线段树维护线性基,但常数大的要命。
一直在卡常,最终卡到1.2s,但是无奈数据太强,三个log终究一分都没得到……
result:
T1:26 T2:10 T3:60
2.反思
T1:
为什么非要头铁去硬上线段树呢……
LYC大佬和我的三个log做法差不多,他用了ST预处理后就能轻松跑过了。
所以有时候不光\(n^2\)的数据需要预处理做,\(nlogn\)的数据也需要用类似ST预处理才能过,尤其是询问数量巨大的时候。
有一个\(|v|\leq 2^4\)的数据范围没看到,这个分也是容易拿到的。
卡常数有时候并不能尽如人意,所以更多的时间应该留给更多部分分的思考,卡常数适当就行了(当然正解另说)。
与J讨论以后,认为我提出的势能分析不可做,原因:没法维护对应分界位置的线性基……
T2:
状压DP应该很容易想到的,还是因为对数学题的恐惧放弃了思考啊……
T3:
正解的规律确实太妙了,感觉自己考场上根本想不出来。
需要的数据结构并不复杂,主要是通过人类智慧找出来规律太难做到了。
不过拿了60分也不错。
3.简要题解
T1:
一句话题意:给出长度为\(n\)的数组,每次询问一个区间\(l,r\),给出参数\(d\),求在\(l\)到\(r\)区间选若干个数与\(d\)异或得到的最大值。
\(n\leq1e6,|v|\leq2^30\)
弱化版:CF1100F Ivan and Burgers,(需要分治,可能是强化版?)
带时间戳线性基模板题,离线询问后扫描线,略。
T2:咕
T3:
一句话题意:给出一个字符串,对所有前缀,求这个串所有子串建出来的fail树的节点深度之和,树根的深度为-1,结果对1e9+7取模。
\(n\leq1e5\)
首先有规律:在原来串的基础上,在末尾补充一个字符,它在所有包含它的fail树中的深度值,等于它与所有原串的后缀拼接起来的字符串在原串出现的次数之和。
如果不想证明的话可以打表,证明的话如果上一次出现的位置dep=0,新字符的dep是1,否则说明上一次出现的位置还能继续匹配相同的前缀,直到dep=0,而这些前缀也必然可以和新加入字符拼接后缀的字符串匹配,可以归纳一下。
得到结论后,对原串跑SAM建出来parent树,对字符串中每个字符对应在parent树上的位置进行到根节点1的路径加,加的值就是该节点的endpos集合大小。
树链剖分维护一下,求答案做两遍前缀和,一遍是dep的前缀和,一遍是最终答案的前缀和,就做完了。
4.总结
1.时间安排
时间不应该浪费在调bug和卡常数这种事情上,有可能浪费了大把时间和精力最后做了无用功。
避免调bug的话,首先应该先确定算法的正确性,算法假了怎么调都是白费功夫;其次就是分模块去调试,实现一个比较麻烦的函数就简单测试一下正确性,否则最后混到一起找谁是错的就要费半天功夫。
避免卡常数的话,数据结构题最常见,FastIO常备,register、inline不厌其烦在写初版代码时就加上,其他回头想到再说。
2.数学题
虽然数学题一直以来都是我的弱项,但是也不能因为不擅长就不敢思考。
数学题数据要么太小要么太大,对于小数据题目,正解往往都是复杂度很高比如\(n^3\)的dp,所以\(n^4\)甚至\(n^5\)这样的高复杂度dp还是要大胆去想的,不能只吃爆搜的保底。
3.带时间戳线性基
有人也叫这个可删除线性基,学到了新知识。
其基本思想有点类似可持久化数据结构,对于每个二进制位打上插入时的时间戳。
做插入操作时,如果此二进制位已经有数了,根据线性基的定义,把这个数更改为当前要插入的数,线性基的功能不会改变,但是比原来数插入时间小的询问不可以使用当前这位的线性基,所以需要更新时间戳。
查询时就像可持久化数据结构一样,比较时间戳就好了。
模板代码:
struct Basic{
int buc[30],tim[30];
void insert(int x,int t){
for(int i=29;i>=0;i--){
if((x>>i)&1){
if(buc[i]){
if(tim[i]<t){
swap(buc[i],x);swap(tim[i],t);
}
x^=buc[i];
}
else{
buc[i]=x;tim[i]=t;
break;
}
}
}
}
int get(int x,int lim){
for(int i=29;i>=0;i--){
if((x^buc[i])>x&&tim[i]>=lim)
x^=buc[i];
}
return x;
}
}B;

浙公网安备 33010602011771号