NOI2011阿狸的打字机
昨天晚上yy出了一个做法后,感觉...好难打啊...,于是先回去休息。今天来打时,还是感觉细节好多,于是就打了两个小时。打完过了编译后,居然过了样例,直接交,尼玛居然过了???......还好自己没有犯什么错误,不然就调死了...
进入正题
询问(x,y)时,就相当于在fail树中问x这个单词节点的子树中有多少个是y的前缀。
先建AC自动机,显然暴力一个词一个词的插入是不行的,可以慢慢的把trie树建出来,就是与别的单词重复的节点就不去建了,直接从以前从没出现的地方开始建(记一下是从trie上的哪个节点进入"空节点"的即可),删除就往父亲跳,加入就往对应的儿子跳。
询问操作让我们想到线段树合并(若节点o是y的前缀,则o这棵线段树上的y处要+1),把询问离线,挂在x对应的单词节点即可完成询问,现在的问题是如何处理"y的所有前缀"这一信息。
我的做法是对于AC自动机上的一个节点,所有以它作为前缀的串是若干个区间组成的,我们把这个修改区间挂在它对应的fail树节点上,然后若要使这个节点多一个修改区间,那么就要有一个删除操作,因此所有挂的修改区间是O(n)级别的,这样就解决了这个问题。
最后就很简单了,直接对fail树dfs一遍,对于节点o,把它的儿子合并给它,然后把要改的区间改了,直接把贡献加到对应的答案里就可以啦。
细节好像有点复杂,为什么别人都2KB AC,而我打了4KB??? 想复杂了以及代码太丑
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<vector>
#include<algorithm>
#include<cmath>
#include<queue>
#define P puts("lala")
#define cp cerr<<"lala"<<endl
#define ln putchar('\n')
#define pb push_back
#define fi first
#define se second
#define mkp make_pair
using namespace std;
inline int read()
{
char ch=getchar();int g=1,re=0;
while(ch<'0'||ch>'9') {if(ch=='-')g=-1;ch=getchar();}
while(ch<='9'&&ch>='0') re=(re<<1)+(re<<3)+(ch^48),ch=getchar();
return re*g;
}
typedef long long ll;
typedef pair<int,int> pii; const int N=100050;
int son[N][26],fail[N],rt=1,sz=1,now=1,dep[N],from=1,father[N],tot=0,fnl[N];
int End[N],st[N];
vector<pii>ve[N],ask[N]; //ac-automation begin:
void insert(int o,char *s,int l,int r) //[l,r)
{
tot++;
for(int i=l;i<r;++i)
{
int x=s[i]-'a';
if(!son[o][x]) son[o][x]=++sz,dep[sz]=dep[o]+1,father[sz]=o;
o=son[o][x];
st[o]=tot;
}
from=0; now=o; fnl[tot]=o; End[o]++;
}
queue<int>q;
void buildfail()
{
q.push(rt);
while(!q.empty())
{
int o=q.front(); q.pop();
for(int i=0;i<26;++i)
{
int v=son[o][i];
if(!v) continue;
if(o==rt) fail[v]=rt;
else
{
int p=fail[o];
while(p)
{
if(son[p][i]) {fail[v]=son[p][i];break;}
p=fail[p];
}
if(!p) fail[v]=rt;
}
q.push(v);
}
}
}
//ac-automation end. //Segment tree
int root[N],ch[N*40][2],add[N*40],cnt=0; //need to modify
vector<int>stk;
inline int newnode()
{
if(stk.size())
{
int x=stk[stk.size()-1];
stk.pop_back();
ch[x][0]=ch[x][1]=add[x]=0;
return x;
}
else {cnt++;return cnt;}
}
void update(int &o,int l,int r,int x,int y) //[x,y]++
{
if(!o) o=newnode();
if(x<=l&&r<=y) {add[o]++;return ;}
int mid=l+r>>1;
if(x<=mid) update(ch[o][0],l,mid,x,y);
if(y>mid) update(ch[o][1],mid+1,r,x,y);
}
int query(int o,int l,int r,int x)
{
if(!o) return 0;
if(l==r) return add[o];
int mid=l+r>>1;
if(x<=mid) return query(ch[o][0],l,mid,x)+add[o];
else return query(ch[o][1],mid+1,r,x)+add[o];
}
int merge(int x1,int x2,int l,int r)
{
if(!x1) return x2;if(!x2) return x1;
add[x1]+=add[x2];
if(l==r) {stk.pb(x2);return x1;}
int mid=l+r>>1;
ch[x1][0]=merge(ch[x1][0],ch[x2][0],l,mid);
ch[x1][1]=merge(ch[x1][1],ch[x2][1],mid+1,r);
stk.pb(x2); return x1;
} //fail tree and answer
int ans[N];
vector<int>G[N];
void dfs(int u,int fa)
{
for(int i=0,siz=G[u].size();i<siz;++i)
{
int v=G[u][i];
if(v==fa) continue;
dfs(v,u);
root[u]=merge(root[u],root[v],1,tot);
}
for(int i=0,siz=ve[u].size();i<siz;++i)
{
int l=ve[u][i].fi,r=ve[u][i].se;
if(l<=r) update(root[u],1,tot,l,r);
}
for(int i=0,siz=ask[u].size();i<siz;++i)
ans[ask[u][i].fi]+=query(root[u],1,tot,ask[u][i].se);
} int slen=0;
char in[N],s[N]; //to check
struct CHK
{
int len;
void dfs(int o)
{
if(o==rt) len=0;
if(End[o]) for(int cas=1;cas<=End[o];++cas)
{
for(int i=0;i<len;++i) putchar(s[i]);ln;
}
for(int i=0;i<26;++i) if(son[o][i])
{
s[len++]=i+'a';
dfs(son[o][i]);
len--;
}
}
}chk; int main()
{
#ifndef ONLINE_JUDGE
freopen("1.in","r",stdin);freopen("1.out","w",stdout);
#endif
int i,j,opt,T;
scanf("%s",in);
int len=strlen(in);
dep[rt]=0;
for(i=0;i<len;++i)
{
if('a'<=in[i]&&in[i]<='z') //add a letter
{
s[slen++]=in[i];
if(!now) ;
else if(!son[now][in[i]-'a']) from=now,now=0;
else now=son[now][in[i]-'a'],st[now]=tot+1;
}
else if(in[i]=='B') //delete the last letter
{
slen--;
if(!now&&slen>dep[from]) ;
else if(!now&&slen==dep[from]) now=from,from=0;
else
{
ve[now].pb(mkp(st[now],tot));
//if(!st[now]) P;//
st[now]=0;
now=father[now];
}
}
else if(in[i]=='P') //print
{
if(now) tot++,fnl[tot]=now,End[now]++;
else insert(from,s,dep[from],slen);
}
} for(i=1;i<=sz;++i) if(st[i]) ve[i].pb(mkp(st[i],tot));
buildfail();
//chk.dfs(rt);
for(i=1;i<=sz;++i) if(fail[i]) G[fail[i]].pb(i);
//build fail-tree T=read();
for(i=1;i<=T;++i)
{
int x=read(),y=read();
ask[fnl[x]].pb(mkp(i,y));
}
dfs(rt,0);
for(i=1;i<=T;++i) printf("%d\n",ans[i]);
return 0;
}
/* */
NOI2011阿狸的打字机的更多相关文章
- BZOJ 2434: [Noi2011]阿狸的打字机 [AC自动机 Fail树 树状数组 DFS序]
2434: [Noi2011]阿狸的打字机 Time Limit: 10 Sec Memory Limit: 256 MBSubmit: 2545 Solved: 1419[Submit][Sta ...
- BZOJ 2434: [Noi2011]阿狸的打字机( AC自动机 + DFS序 + 树状数组 )
一个串a在b中出现, 那么a是b的某些前缀的后缀, 所以搞出AC自动机, 按fail反向建树, 然后查询(x, y)就是y的子树中有多少是x的前缀. 离线, 对AC自动机DFS一遍, 用dfs序+树状 ...
- [NOI2011]阿狸的打字机(好题!!!!)
2785: [NOI2011]阿狸的打字机 Time Limit: 1 Sec Memory Limit: 128 MBSubmit: 7 Solved: 3[Submit][Status][We ...
- P2414 [NOI2011]阿狸的打字机
P2414 [NOI2011]阿狸的打字机 AC自动机+树状数组 优质题解 <------题目分析 先AC自动机搞出Trie图 然后根据fail指针建一只新树 把树映射(拍扁)到一个序列上,用树 ...
- 【BZOJ2434】[NOI2011]阿狸的打字机 AC自动机+DFS序+树状数组
[BZOJ2434][NOI2011]阿狸的打字机 Description 阿狸喜欢收藏各种稀奇古怪的东西,最近他淘到一台老式的打字机.打字机上只有28个按键,分别印有26个小写英文字母和'B'.'P ...
- [NOI2011]阿狸的打字机 --- AC自动机 + 树状数组
[NOI2011] 阿狸的打字机 题目描述: 阿狸喜欢收藏各种稀奇古怪的东西,最近他淘到一台老式的打字机. 打字机上只有28个按键,分别印有26个小写英文字母和'B'.'P'两个字母.经阿狸研究发现, ...
- BZOJ2434 [Noi2011]阿狸的打字机 【AC自动机 + fail树 + 树状数组】
2434: [Noi2011]阿狸的打字机 Time Limit: 10 Sec Memory Limit: 256 MB Submit: 3610 Solved: 1960 [Submit][S ...
- BZOJ2434: [NOI2011]阿狸的打字机(AC自动机+dfs序+树状数组)
[NOI2011]阿狸的打字机 题目链接:https://www.luogu.org/problemnew/show/P2414 题目背景 阿狸喜欢收藏各种稀奇古怪的东西,最近他淘到一台老式的打字机. ...
- bzoj 2434 [Noi2011]阿狸的打字机 AC自动机
[Noi2011]阿狸的打字机 Time Limit: 10 Sec Memory Limit: 256 MBSubmit: 4001 Solved: 2198[Submit][Status][D ...
- 洛谷 P2414 [NOI2011]阿狸的打字机 解题报告
P2414 [NOI2011]阿狸的打字机 题目背景 阿狸喜欢收藏各种稀奇古怪的东西,最近他淘到一台老式的打字机. 题目描述 打字机上只有28个按键,分别印有26个小写英文字母和'B'.'P'两个字母 ...
随机推荐
- powershell和cmd对比
前言 计算机啊这东西,本质上是硬件和软件的综合体.如果只有硬件没有软件的话,这也是台辣鸡而已.而计算机软件中最靠近硬件的一层,就是操作系统层. 操作系统有很多种,比如Unix/Linux/Mac OS ...
- 技术分享 | MySQL Group Replication集群对IP地址的限制导致的一些问题与解决办法
GreatSQL社区原创内容未经授权不得随意使用,转载请联系小编并注明来源. 1. 遇到问题 测试人员小玲准备在docker环境中部署MGR集群进行一些测试,她有三个容器,容器IP分别是: 172.3 ...
- AI+医疗:使用神经网络进行医学影像识别分析 ⛵
作者:韩信子@ShowMeAI 计算机视觉实战系列:https://www.showmeai.tech/tutorials/46 行业名企应用系列:https://www.showmeai.tech/ ...
- java-方法创建与使用
1.方法: 1)封装一段特定的业务逻辑功能 2)方法尽可能的独立,一个方法只干一件事(低耦合) 3)方法可以被反复调用多次(高复用) 4)减少代码重复,有利于代码维护,有利于团队协作开发2.方法的定义 ...
- Excel 运算符(一):算术运算符
算术运算符用于最基本的加.减.乘.除运算. 运算符 含义 实例 结果 + 加法运算 =2+3 5 - 减法运算 =5-2 3 * 乘法运算 =5*2 10 / 除法运算 =4/2 2 % 百分数 =5 ...
- 从C过渡到C++——换一个视角深入数组[真的存在高效吗?](2)
从C过渡到C++--换一个视角深入数组[真的存在高效吗?](2) C风格高效的数组遍历 在过渡到C++之前我还是想谈一谈如何书写高效的C的代码,这里的高效指的是C代码的高效,也就是在不开启编译器优化下 ...
- 三道MySQL联合索引面试题,淘汰80%的面试者,你能答对几道
众所周知MySQL联合索引遵循最左前缀匹配原则,在少数情况下也会不遵循(有兴趣,可以翻一下上篇文章). 创建联合索引的时候,建议优先把区分度高的字段放在第一列. 至于怎么统计区分度,可以按照下面这种方 ...
- html js 导出excel表格
这个使用js 导出excel,可以集成其他语言,可以html,php,asp ,java 等,自己喜欢用那种语言就用哪种,使用非常方便.js是使用tableExport.js ,jquery-3.2. ...
- CCF NOI Online 2021 提高组 T3 岛屿探险(CDQ 分治,Trie 树)
题面 凇睦是一个喜欢探险的女孩子,这天她到一片海域上来探险了. 在这片海域上一共有 n 座岛屿排成一排,标号为 1, 2, 3, . . . , n.每座岛屿有两个权值,分别为劳累度 ai 和有趣度 ...
- CF165D Beard Graph(dfs序+树状数组)
题面 题解 乍一看,单点修改,单链查询,用树链剖分维护每条链上白边的数量就完了, 还是--得写树链剖分吗?--3e5,乘两个log会T吗-- (双手颤抖) (纠结) 不!绝不写树链剖分! 这题如果能维 ...