首先发现这个插入的非常有特点,我们可以直接利用这个特殊的性质在\(Trie\)树上模拟指针的进退

之后得到了\(Trie\)树,先无脑建出\(AC\)机

之后考虑一下如何写暴力

最简单的暴力对于每一个询问直接在\(AC\)机上匹配之后跳\(fail\),跳到多少次\(fail\)就代表出现了几次

显然这并不能通过

考虑一下优雅的跳\(fail\)

发现\(fail\)指针建出来恰好是一个树的结构,因为一个点的\(fail\)只能指向唯一的一个点

把这样一棵\(fail\)树建出来,我们直接在\(fail\)树上判断另一个串的结束标记是否在这个点到根的路径上就好了

可以对\(fail\)树搞一个\(dfs\)序,之后把问题转化为单点加,区间查显然可以直接用一个树状数组来维护

但是这个样子还是要对每一个串都进行一遍这样的操作

但是考虑到每一个查询操作有很多共用的节点,我们可以直接按照\(Trie\)上的顺序离线下来,之后利用大量重复的这一特性去统计答案

代码

#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<queue>
#define re register
#define maxn 100005
#define LL long long
#define max(a,b) ((a)>(b)?(a):(b))
#define min(a,b) ((a)<(b)?(a):(b))
#define lowbit(x) ((x)&(-x))
struct E
{
int v,nxt;
}e[maxn<<1];
int head[maxn],fa[maxn];
int n,m,to[maxn],dfn[maxn],DFN[maxn];
int cnt,num,tot,__,t;
struct Ask
{
int x,y,rk,ans;
}a[maxn];
inline int read()
{
char c=getchar();
int x=0;
while(c<'0'||c>'9') c=getchar();
while(c>='0'&&c<='9') x=(x<<3)+(x<<1)+c-48,c=getchar();
return x;
}
inline void add_edge(int x,int y)
{
e[++num].v=y;
e[num].nxt=head[x];
head[x]=num;
}
char S[maxn];
int c[maxn],sum[maxn];
inline void add(int x,int val){for(re int i=x;i<=cnt+1;i+=lowbit(i)) c[i]+=val;}
inline int ask(int x){int now=0;for(re int i=x;i;i-=lowbit(i)) now+=c[i];return now;}
int son[maxn][26],trie[maxn][26],fail[maxn],Ans[maxn];
inline void ins()
{
int now=0;
fa[now]=0;
scanf("%s",S+1);
int len=strlen(S+1);
for(re int i=1;i<=len;i++)
{
if(S[i]=='B')
{
now=fa[now];
continue;
}
if(S[i]=='P')
{
to[++tot]=now;
continue;
}
if(!son[now][S[i]-'a']) son[now][S[i]-'a']=trie[now][S[i]-'a']=++cnt,fa[cnt]=now;
now=son[now][S[i]-'a'];
}
}
inline void Build()
{
std::queue<int> q;
for(re int i=0;i<26;i++) if(son[0][i]) q.push(son[0][i]);
while(!q.empty())
{
int k=q.front();q.pop();
for(re int i=0;i<26;i++)
if(son[k][i]) fail[son[k][i]]=son[fail[k]][i],q.push(son[k][i]);
else son[k][i]=son[fail[k]][i];
}
}
inline int cmp(Ask A,Ask B)
{
return dfn[A.x]<dfn[B.x];
}
void DFS(int x)
{
dfn[x]=++__;
if(x!=fail[x]) add_edge(fail[x],x);//printf("%d\n",x);
for(re int i=0;i<26;i++)
if(trie[x][i]) DFS(trie[x][i]);
}
void dfs(int x,int F)
{
sum[x]=1;DFN[x]=++__;
for(re int i=head[x];i;i=e[i].nxt)
if(e[i].v!=F)
{
dfs(e[i].v,x);
sum[x]+=sum[e[i].v];
}
}
void Dfs(int x)
{
__++;
add(DFN[x],1);
while(a[t].x==x)
{
int Y=to[a[t].y];
a[t].ans=ask(DFN[Y]+sum[Y]-1)-ask(DFN[Y]-1);
t++;
}
for(re int i=0;i<26;i++)
if(trie[x][i]) Dfs(trie[x][i]);
add(DFN[x],-1);
}
int main()
{
ins();
Build(),DFS(0);
n=read();
for(re int i=1;i<=n;i++) a[i].x=read(),a[i].y=read(),a[i].rk=i,std::swap(a[i].x,a[i].y),a[i].x=to[a[i].x];
std::sort(a+1,a+n+1,cmp);
__=0,dfs(0,0),__=0;t=1;
Dfs(0);
for(re int i=1;i<=n;i++) Ans[a[i].rk]=a[i].ans;
for(re int i=1;i<=n;i++) printf("%d\n",Ans[i]);
return 0;
}

【[NOI2011]阿狸的打字机】的更多相关文章

  1. BZOJ 2434: [Noi2011]阿狸的打字机 [AC自动机 Fail树 树状数组 DFS序]

    2434: [Noi2011]阿狸的打字机 Time Limit: 10 Sec  Memory Limit: 256 MBSubmit: 2545  Solved: 1419[Submit][Sta ...

  2. BZOJ 2434: [Noi2011]阿狸的打字机( AC自动机 + DFS序 + 树状数组 )

    一个串a在b中出现, 那么a是b的某些前缀的后缀, 所以搞出AC自动机, 按fail反向建树, 然后查询(x, y)就是y的子树中有多少是x的前缀. 离线, 对AC自动机DFS一遍, 用dfs序+树状 ...

  3. [NOI2011]阿狸的打字机(好题!!!!)

    2785: [NOI2011]阿狸的打字机 Time Limit: 1 Sec  Memory Limit: 128 MBSubmit: 7  Solved: 3[Submit][Status][We ...

  4. P2414 [NOI2011]阿狸的打字机

    P2414 [NOI2011]阿狸的打字机 AC自动机+树状数组 优质题解 <------题目分析 先AC自动机搞出Trie图 然后根据fail指针建一只新树 把树映射(拍扁)到一个序列上,用树 ...

  5. 【BZOJ2434】[NOI2011]阿狸的打字机 AC自动机+DFS序+树状数组

    [BZOJ2434][NOI2011]阿狸的打字机 Description 阿狸喜欢收藏各种稀奇古怪的东西,最近他淘到一台老式的打字机.打字机上只有28个按键,分别印有26个小写英文字母和'B'.'P ...

  6. [NOI2011]阿狸的打字机 --- AC自动机 + 树状数组

    [NOI2011] 阿狸的打字机 题目描述: 阿狸喜欢收藏各种稀奇古怪的东西,最近他淘到一台老式的打字机. 打字机上只有28个按键,分别印有26个小写英文字母和'B'.'P'两个字母.经阿狸研究发现, ...

  7. BZOJ2434 [Noi2011]阿狸的打字机 【AC自动机 + fail树 + 树状数组】

    2434: [Noi2011]阿狸的打字机 Time Limit: 10 Sec  Memory Limit: 256 MB Submit: 3610  Solved: 1960 [Submit][S ...

  8. BZOJ2434: [NOI2011]阿狸的打字机(AC自动机+dfs序+树状数组)

    [NOI2011]阿狸的打字机 题目链接:https://www.luogu.org/problemnew/show/P2414 题目背景 阿狸喜欢收藏各种稀奇古怪的东西,最近他淘到一台老式的打字机. ...

  9. bzoj 2434 [Noi2011]阿狸的打字机 AC自动机

    [Noi2011]阿狸的打字机 Time Limit: 10 Sec  Memory Limit: 256 MBSubmit: 4001  Solved: 2198[Submit][Status][D ...

  10. 洛谷 P2414 [NOI2011]阿狸的打字机 解题报告

    P2414 [NOI2011]阿狸的打字机 题目背景 阿狸喜欢收藏各种稀奇古怪的东西,最近他淘到一台老式的打字机. 题目描述 打字机上只有28个按键,分别印有26个小写英文字母和'B'.'P'两个字母 ...

随机推荐

  1. golang数组与切片

    golang中坑的慢慢踩! golang中的数组是值类型,函数调用是传入的是数组的拷贝,如果想改变数组的值,可考虑使用指针数组,即函数调用时传入数组的地址 golang中的切片是引用类型,但是在函数中 ...

  2. 4、构造方法、this、super

    构造方法 构造方法引入 * A:构造方法的引入 在开发中经常需要在创建对象的同时明确对象的属性值,比如员工入职公司就要明确他的姓名.年龄等属性信息. 那么,创建对象就要明确属性值,那怎么解决呢?也就是 ...

  3. java自学-流程控制语句

    一件事情从开始到结束,需要先做什么,再做什么,最后再怎么做,这段过程就是一个流程.程序逻辑处理也是一个流程,java中有专门的流程控制语句,主要分为这几种:顺序,判断,选择,循环. 1.顺序流程  顺 ...

  4. Ubuntu14.04默认cmake升级为3.x

    由于Ubuntu14.04的cmake版本为2.8.x,而如果需要cmake3.x版本时,无法生成makefile,有两种方法可以安装cmake3.4.1: sudo apt-get install ...

  5. Spring动态注册bean实现动态多数据源

    Spring动态注册bean实现动态多数据源 http://blog.csdn.net/littlechang/article/details/8071882

  6. 【SSH网上商城项目实战27】域名空间的申请和项目的部署及发布

     转自:https://blog.csdn.net/wwww_com/article/details/54405355 前面陆陆续续的完成了网上商城的一些基本功能,虽然还有很多地方有待完善,但是不影响 ...

  7. visualvm 插件 visual gc 使用介绍

    visual gc 是 visualvm 中的图形化查看 gc 状况的插件. 具体详细介绍可参照: http://www.oracle.com/technetwork/java/visualgc-13 ...

  8. 轻松学习java可重入锁(ReentrantLock)的实现原理(转 图解)

    前言 相信学过java的人都知道 synchronized 这个关键词,也知道它用于控制多线程对并发资源的安全访问,兴许,你还用过Lock相关的功能,但你可能从来没有想过java中的锁底层的机制是怎么 ...

  9. Spring的自动装配Bean

    spring的自动装配功能的定义:无须在Spring配置文件中描述javaBean之间的依赖关系(如配置<property>.<constructor-arg>).IOC容器会 ...

  10. MyEclipse中设置开发项目时使用的默认JDK

    安装好MyEclipse之后,在MyEclipse中开发项目时,默认使用的是MyEclipse是自带的JDK,如下图所示: 如果我们需要使用自己安装好的JDK,那么就需要在MyEclipse中重新设置 ...