今天主要讲了两大块内容,穿插了很多小知识,嗯,很长知识。
一.AC自动机
(1)Fail指针
搞明白了fail指针的含义(一定要明确):
Fail指针指向的是在Trie中当前结点失配后以当前串的后缀为前缀的串结点,并且最大。
如果不明白这段话很难理解。
这点十分重要,在状态机里的转移和Fail Tree一定要深刻理解它;
(2)AC自动机里的DP
一般来说在自动机里的DP的状态为F[i][cur][flag]表示模板串处理到i,在自动机里为j,某种状态是否符合。
嗯,这个考验DP功底。
(3)数位DP
如果问你你小于N位的串满足什么性质,首先AC自动机,让后按上面说的搞搞(具体情况具体分析)。
可要是问小于N的数中满足什么性质的呢?
如N=9923213
问小于N的数中9的个数比6的多的数
麻烦了·······
我们可以这样想(授课神牛如是说):
首先如果问题简化,小于N位的数中9的个数大于6的,问个数。显然DP
f[i][j]前i个里9比6多j个。
那么我们固定左数第1位为1,
1········搞一下个数
2········
3········
(起始都一样)
6········
f【i][j】的j在取答案时注意一下
7·······
8·······
9最麻烦,特殊考虑
仍旧一样的思想
固定左数第2位(起始是原问题的子问题)
很多数位DP就是这种思想。
BUT有时这还不够,当出题人丧心病狂时,这也会TLE。
怎么办呢?
我们可以发现,在DP时条件被逐渐变严格。所以我们可以和刚在在AC自动机里一样加个第三维flag,表示前i个是否和N的前i个相等
由于具体我还没想太明白····嗯······一个大坑···········
(4)Fail Tree
高端大气上档次啊。
它可以把一些奇怪的字符串问题变成树的维护问题,然后LCT,树链剖分,想怎么搞怎么搞。(神牛的LCT只要1KB,Orz)
首先要明白(真正明白)Fail指针的含义,if(不懂)goto up,我承认我根本没说清楚,因为我只是心里明白
首先我们有两颗Tree,一个是Tree,一个是Fail Tree,
解释一下Fail Tree,它是将每个结点的Fail指针当做边构成的一颗Tree。
如果B是A的ancestor,根据Fail指针的含义,B在Trie中代表的串为A代表的串的后缀,并且在Trie中B有时B的孩子代表的串的前缀。
当一个前缀等于本身时,前缀为这个串。
嗯,讲的相当失败,举个例子吧
NOI阿狸的打字机
给一个Trie,问i结点代表的串在j结点代表的串中出现几次。
暴力?呵呵:-)
本来wikioi上的题解五花八门,连函数式线段树都出来了。可今天神牛说那些修改都是骗人的。
sol:
考虑P[i..j](这里的i,j含义不同上),如果P[i..j]=Q,那么P[1..j]可以通过Fail到达Q(Fail的含义),即在Fail Tree中Q为P[1..j]的祖先,
而在Trie中,如果我考虑j的全部祖先,那么我结合Fail Tree可以考虑P[1..j]的每个子串。不知道大家明白没明白,现在的问题就变成了:
求结点j在Trie中的祖先有多少在Fail Tree中是I的孩子
我只能讲到这种地步了,大概意识是通过Trie去掉前缀,Fail Tree去掉后缀。我承认根本没讲清楚,大家去问神牛吧,蒟蒻自己要晕了······
(5)树上的维护(单点加路径和)
我的第一反应是树链剖分,结果被神牛BS了,嗯,学习了,树的括号表示法。有兴趣百度,我认为这个真的很简单。
(6)Hash求后缀数组
bool cmp(int a,int b){
int lcp=LCP(a,b);
return s[lcp+1]>s[lcp+1];
}
二 分块
没时间,神牛就讲了部分。现在我也没时间写了,就写一点
区间第K大->主席树
区间第K大+单点修改->树状数组套主席树
区间第K大+奇葩修改->分块
凸包+修改->分块(要学习)
另外,分块的题很卡常数,经常一卡40分,不然就和暴力一样了。
所以快的大小不一定为sqrt(n),要具体分析,先设为S,然后算完复杂度后均值不等式。
其实还讲了概率····没时间了写了
就这些,明天把题做了(搞到了神牛重a+b到神题的全部代码和模板o(∩_∩)o 哈哈)