【集训第二天·翻水的老师】--ac自动机+splay树
今天是第二天集训。(其实已经是第三天了,只是昨天并没有机会来写总结,现在补上)
上午大家心情都很愉快,因为老师讲了splay树和ac自动机。
但到了下午,我们的教练竟然跑出去耍了(excuse me?),害的我们在一些不懂的地方冥思苦想浪费时间,效率极其低下,所以说只做了点模板题,以后这方面的知识还需要多多练习0.0
1.ac自动机
这东西是kmp的升级版本,由一个模式串升级到了多个模式串,效率依然高。
只要掌握了kmp,ac自动机一般不会有问题。哦,当然你也必须会trie树,这是自动机的基础
首先,和kmp一样,构造fail数组,也就是next数组。注意:这个fail数组不止能够跳到自己的最大相同前缀上,还能跳到别的串。
有了fail数组,就可以匹配了,过程也和kmp类似,只不过加一些处理令得模式串之间可以互相到达
所以,以上我说的全是废话[滑稽]。
话不多说,上代码(标准模板)
CODE: HDU2222
#include<bits/stdc++.h>
using namespace std;
#define N 10005
int tr[N*][],ls[N*],n,tot,f[N*],val[N*],ans;
char s[],ch[]; void adtr(){
int i=,x=;
while(ch[i]){
int k=ch[i]-'a';
if(!tr[x][k])tr[x][k]=++tot;
x=tr[x][k];i++;
}
val[x]++;
} void getfail(){
queue<int>q;
int u,v,rt;
for(int i=;i<;i++)if(tr[][i])q.push(tr[][i]),f[tr[][i]]=ls[tr[][i]]=;
while(!q.empty()){
rt=q.front();q.pop();
for(int i=;i<;i++){
int u=tr[rt][i];
if(!u){tr[rt][u]=tr[f[rt]][u];continue;}
q.push(u);
v=f[rt];
while(v&&!tr[v][i])v=f[v];
f[u]=tr[v][i];
if(val[f[u]])ls[u]=f[u];
else ls[u]=ls[f[u]];
}
}
} void make(int x){
if(!x)return;
if(val[x])ans+=val[x],val[x]=;
make(ls[x]);
} void ac(){
int x=;
for(int i=;s[i];i++){
int k=s[i]-'a';
//while(x&&!tr[x][k])x=f[x];
/*上一句可以省略的原因是 getfail()函数中的这一句 :
if(!u){tr[rt][u]=tr[f[rt]][u];continue;}
已经通过迭代求出了最近的有k的串 */
x=tr[x][k];
if(val[x])make(x);
else if(ls[x])make(ls[x]);
}
} int main(){
int T;cin>>T;
while(T--){
memset(val,,sizeof(val));
memset(tr,,sizeof(tr));
ans=tot=;scanf("%d",&n);
for(int i=;i<=n;i++)scanf("%s",ch),adtr();
getfail();scanf("%s",s);ac();
printf("%d\n",ans);
}
}
点击查看完整版。。
2.ac自动机+dp
就是字符串禁用一类的题
正常的字符串禁用只有一个串禁用,求长度为n的不含禁用串的字符串由多少个
但由于学了ac自动机,我们可以干出一个多串禁用版本,非常high,如果串的长度n太大,dp是还需要使用矩阵快速幂,high到爆!!可惜我不会~
这东西暂时不管,之后再来慢慢学。
3.splay树
伸展树,区间王,线段树能做的区间题它都能做(据说是这样),线段树不能做的它也可以做
原因:线段树是静态树,不能插入、删除和区间倒置、平移之类的,如果出现上述操作,必定会导致重新建一棵线段树,这时就可以用splay动态树。
splay如何动态维护区间?
和线段树不同,splay树上的节点都是只表示线段上的一个点,一般是区间中点,但这并不妨碍它维护区间信息。在需要进行动态操作时,我们把和操作有关的点给旋转至根节点,再在根节点上进行操作,这样就不会对原树造成太大影响。
值得一说的是,splay树是一棵二叉平衡树,也就是说,对于任何一个节点,它都满足一个式子:lson<fa<rson ,这样就可以通过某种手段表示一个区间。
把某个节点旋转至根有一些特定的操作,称splay。而在splay树中,几乎每个操作都和splay有关,splay操作中有个rotate操作,表示把某个点和它的父亲节点交换位置而不影响原树的性质(平衡),这就有些屌。
详细图解参考 http://www.cnblogs.com/Paul-Guderian/p/6637045.html
吐槽一下:水友做的总是比我详细得多[滑稽],劳资根本就不会画图
另外呢,推荐一个pdf,有助于详细得理解伸展树
https://wenku.baidu.com/view/7f0ff024ccbff121dd3683ac.html
一道题 NOI2005 维修数列
这个题几乎包含了所有伸展树的操作,打完了它你会有一种莫名的成就感。。(至少我是这样)
具体参考黄学长 http://hzwer.com/2841.html
自己暂时打不出来,在黄学长的代码上加了注释
CODE:
#include<bits/stdc++.h>
#define N 1000005
#define inf 1000000000
using namespace std;
int v[N],sum[N],lx[N],mx[N],id[N],size[N],rx[N],fa[N],a[N],n,m,rt,cnt,tag[N],rev[N],c[N][];
queue<int>q;
int read(){
char c;int f=,x=;c=getchar();
while(c>''||c<''){if(c=='-')f=-;c=getchar();}
while(c<=''&&c>='')x=x*+c-'',c=getchar();
return f*x;
} void update(int x)//上传信息
{
int l=c[x][],r=c[x][];
sum[x]=sum[l]+sum[r]+v[x];
size[x]=size[l]+size[r]+;
mx[x]=max(mx[l],mx[r]);
mx[x]=max(mx[x],rx[l]+v[x]+lx[r]);
lx[x]=max(lx[l],sum[l]+v[x]+lx[r]);
rx[x]=max(rx[r],sum[r]+v[x]+rx[l]);
} void pushdown(int x)//下放lazy标记
{
int l=c[x][],r=c[x][];
if(tag[x])
{
rev[x]=tag[x]=;
if(l)tag[l]=,v[l]=v[x],sum[l]=v[x]*size[l];
if(r)tag[r]=,v[r]=v[x],sum[r]=v[x]*size[r];
if(v[x]>=)
{
if(l)lx[l]=rx[l]=mx[l]=sum[l];
if(r)lx[r]=rx[r]=mx[r]=sum[r];
}
else
{
if(l)lx[l]=rx[l]=,mx[l]=v[x];
if(r)lx[r]=rx[r]=,mx[r]=v[x];
}
}
if(rev[x])
{
rev[x]^=;rev[l]^=;rev[r]^=;
swap(lx[l],rx[l]);swap(lx[r],rx[r]);
swap(c[l][],c[l][]);swap(c[r][],c[r][]);
}
} void rotate(int x,int &k){//旋转操作
int y=fa[x],z=fa[y],l,r;
if(c[y][]==x)l=;else l=;r=l^;
if(y==k)k=x;
else c[z][c[z][]==y]=x;
fa[x]=z;fa[c[x][r]]=y;fa[y]=x;
c[y][l]=c[x][r];c[x][r]=y;
update(y),update(x);
} void splay(int x,int &k){//把某个节点转至根
while(x!=k){
int y=fa[x],z=fa[y];
if(y!=k){
if((c[z][]==y)^(c[y][]==x))rotate(x,k);
else rotate(y,k);
}
rotate(x,k);
}
} int find(int x,int rk){//查找序列内第k个数的节点编号并把它调整至根
pushdown(x);
int l=c[x][],r=c[x][];
if(size[l]+==rk)return x;
if(size[l]>=rk)return find(l,rk);
return find(r,rk-size[l]-);
} int split(int k,int tot){//分离出某个子树(一段区间),便于操作
int x=find(rt,k),y=find(rt,k+tot+);
splay(x,rt);splay(y,c[x][]);
return c[y][];
} void query(int k,int tot){//查询,题目特殊
int x=split(k,tot);
printf("%d\n",sum[x]);
} void modify(int k,int tot,int val){//把某一区间所有数都修改为val
int x=split(k,tot),y=fa[x];
v[x]=val;tag[x]=;sum[x]=size[x]*val;
if(val>=)lx[x]=rx[x]=mx[x]=sum[x];
else lx[x]=rx[x]=,mx[x]=val;
update(y);update(fa[y]);
} void rever(int k,int tot){//倒置某一区间
int x=split(k,tot),y=fa[x];
if(!tag[x]){
rev[x]^=;
swap(c[x][],c[x][]);
swap(lx[x],rx[x]);
update(y);update(fa[y]);
}
} void rec(int x){//删除子树空间,节省空间
if(!x)return;
int l=c[x][],r=c[x][];
rec(l);rec(r);q.push(x);
fa[x]=tag[x]=rev[x]=c[x][]=c[x][]=;
} void erase(int k,int tot){//删除某一区间
int x=split(k,tot),y=fa[x];
rec(x);c[y][]=;
update(y);update(fa[y]);
} void build(int l,int r,int f){//建立树
if(l>r)return;
int mid=(l+r)>>,now=id[mid],last=id[f];
if(l==r){
sum[now]=a[l];size[now]=;
tag[now]=rev[now]=;
if(a[l]>=)lx[now]=rx[now]=mx[now]=a[l];
else lx[now]=rx[now]=,mx[now]=a[l];
}
else build(l,mid-,mid),build(mid+,r,mid);
v[now]=a[mid];fa[now]=last;update(now);
c[last][mid>=f]=now;
} void insert(int k,int tot){//插入新的一段区间
for(int i=;i<=tot;i++)a[i]=read();
for(int i=;i<=tot;i++)
if(!q.empty())id[i]=q.front(),q.pop();//这里使用了以前被删除的区间节点标号
else id[i]=++cnt;//若被删除的区间节点不够,增加新的标号值
build(,tot,);int z=id[(+tot)>>];//对于新区间,建立一棵树,把这棵树加到总树中
int x=find(rt,k+),y=find(rt,k+);
splay(x,rt);splay(y,c[x][]);
fa[z]=y;c[y][]=z;
update(y);update(x);
} int main(){
n=read();m=read();
mx[]=a[]=a[n+]=-inf;
for(int i=;i<=n;i++)a[i+]=read();
for(int i=;i<=n+;i++)id[i]=i;
build(,n+,);
rt=(n+)>>;cnt=n+;
int k,tot,val;
char ch[];
while(m--){
scanf("%s",ch);
if(ch[]!='M'||ch[]!='X')k=read(),tot=read();
if(ch[]=='I')insert(k,tot);
if(ch[]=='D')erase(k,tot);
if(ch[]=='M')
{
if(ch[]=='X')printf("%d\n",mx[rt]);
else val=read(),modify(k,tot,val);
}
if(ch[]=='R')rever(k,tot);
if(ch[]=='G')query(k,tot);
} return ;
}
下午老师出去玩了,我们很多没懂的地方想问也没办法,最后几个人一起讨论了一晚上,真的是,shit。还有,有些上信息课的傻屌把劳资主机搞烂了,还好我机智的把所有资料转移到了百度云,不然估计是要暴走了。
晚上发生了一些搞笑的事,一哥们儿晚上吃完饭打游戏,没关后门,被hy逮住了。更扯淡的是,那时候我在看小说,听到他被老师训斥的声音才发觉有人来了。也就是说,他为我挡了一刀。。
好了,第二天就这样了。
【集训第二天·翻水的老师】--ac自动机+splay树的更多相关文章
- hdu 4117 GRE Words (ac自动机 线段树 dp)
参考:http://blog.csdn.net/no__stop/article/details/12287843 此题利用了ac自动机fail树的性质,fail指针建立为树,表示父节点是孩子节点的后 ...
- hdu 4117 -- GRE Words (AC自动机+线段树)
题目链接 problem Recently George is preparing for the Graduate Record Examinations (GRE for short). Obvi ...
- 【BZOJ2434】阿狸的打字机(AC自动机,树状数组)
[BZOJ2434]阿狸的打字机(AC自动机,树状数组) 先写个暴力: 每次打印出字符串后,就插入到\(Trie\)树中 搞完后直接搭\(AC\)自动机 看一看匹配是怎么样的: 每次沿着\(AC\)自 ...
- 【BZOJ2434】【NOI2011】阿狸的打字机(AC自动机,树状数组)
[BZOJ2434]阿狸的打字机(AC自动机,树状数组) 先写个暴力: 每次打印出字符串后,就插入到\(Trie\)树中 搞完后直接搭\(AC\)自动机 看一看匹配是怎么样的: 每次沿着\(AC\)自 ...
- BZOJ 2434: [Noi2011]阿狸的打字机 [AC自动机 Fail树 树状数组 DFS序]
2434: [Noi2011]阿狸的打字机 Time Limit: 10 Sec Memory Limit: 256 MBSubmit: 2545 Solved: 1419[Submit][Sta ...
- 【BZOJ-2434】阿狸的打字机 AC自动机 + Fail树 + DFS序 + 树状数组
2434: [Noi2011]阿狸的打字机 Time Limit: 10 Sec Memory Limit: 256 MBSubmit: 2022 Solved: 1158[Submit][Sta ...
- BZOJ2434:[NOI2011]阿狸的打字机(AC自动机,线段树)
Description 阿狸喜欢收藏各种稀奇古怪的东西,最近他淘到一台老式的打字机.打字机上只有28个按键,分别印有26个小写英文字母和'B'.'P'两个字母. 经阿狸研究发现,这个打字机是这样工作的 ...
- BZOJ2434 [Noi2011]阿狸的打字机 【AC自动机 + fail树 + 树状数组】
2434: [Noi2011]阿狸的打字机 Time Limit: 10 Sec Memory Limit: 256 MB Submit: 3610 Solved: 1960 [Submit][S ...
- Codeforces 163E(ac自动机、树状数组)
要点 显然ac自动机的板子就可以暴力一下答案了 为了优化时间复杂度,考虑套路fail树的dfs序.发现本题需要当前这个尾点加上所有祖先点的个数,考虑使用树状数组差分一下,在父点+1,在子树后-1,每次 ...
随机推荐
- Digilent Xilinx USB Jtag cable
Digilent Xilinx USB Jtag cable 安装环境 操作系统:fedora 20 64bit 源链接:https://wiki.gentoo.org/wiki/Xilinx_USB ...
- vue 内联样式style中的background
在我们使用vue开发的时候 有很多时候我们需要用到背景图 这个时候会直接使用 内联样式 直接把你拿到的数据拼接上去 注意 在vue中直接使用style时 花括号一定别忘记 还有就是你的url一定 ...
- hexo博客图片问题
hexo博客图片问题 第一步 首先确认_config.yml 中有 post_asset_folder:true. Hexo 提供了一种更方便管理 Asset 的设定:post_asset_folde ...
- 微信开发之SVN提交代码与FTP同步到apache的根目录
SVN是协同开发的,版本控制器,就是几个人同时开发,可以提交代码到SVN服务器,这样就可以协同开发,一般是早上上班首先更新下代码,然后自己修改代码 工作一天之后,修改代码之后,下班之前,更新代码,然后 ...
- Class-Based-View(CBV)
我们都知道,Python是一个面向对象的编程语言,如果只用函数来开发,有很多面向对象的优点就错失了(继承.封装.多态).所以Django在后来加入了Class-Based-View.可以让我们用类写V ...
- Hey,man,are you ok? -- 关于心跳、故障监测、lease机制
电话之于短信.微信的一个很大的不同点在于,前者更加及时,有更快速直接的反馈:而后面两个虽然称之为instant message,但经常时发出去了就得等对方回复,等多久是不确定的.打电话能明确知道对方在 ...
- 分布式服务框架HSF
最近在读阿里巴巴中台战略思想与架构这本书,so和大家分享一些我get到的东东. HSF是阿里巴巴内部的分布式服务框架,这个大家都很熟悉了,先上一张HSF的工作原理图: 这个图说明了HSF框架中每个组件 ...
- ICC_lab总结——ICC_lab5:布线&&数字集成电路物理设计学习总结——布线
字丑,禁止转载! 这里将理论总结和实践放在一起了. 布线的理论总结如下所示: 下面是使用ICC进行实践的流程: 本次的布线实验主要达成的目标是: ·对具有时钟树布局后的设计进行可布线性检查 ·完成布线 ...
- JavaScript的基础学习
由js和python想到的: 弱类型语言 js 中的数据在进行算数运算时,会自动转换类型强类型语言 变量的值的数据类型一旦确定,使用时不能改变 动态语言:编译时不知道数据类型,只有在执行时才知道数据类 ...
- 不错的ngix/redis/java/android学习地址
http://blog.csdn.net/xlgen157387/article/details/50051543 徐刘根的博客,好像是“Java后端技术”微信公众号的建立者,反正看到不少关于他的博文 ...