您的位置:首页 > 其它

[bzoj2434][ac自动机][线段树合并][Noi2011]阿狸的打字机

2018-02-07 19:04 567 查看
2434: [Noi2011]阿狸的打字机

Time Limit: 10 Sec Memory Limit: 256 MB

Submit: 3803 Solved: 2081

[Submit][Status][Discuss]

Description

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

经阿狸研究发现,这个打字机是这样工作的:

l 输入小写字母,打字机的一个凹槽中会加入这个字母(这个字母加在凹槽的最后)。

l 按一下印有’B’的按键,打字机凹槽中最后一个字母会消失。

l 按一下印有’P’的按键,打字机会在纸上打印出凹槽中现有的所有字母并换行,但凹槽中的字母不会消失。

例如,阿狸输入aPaPBbP,纸上被打印的字符如下:

a

aa

ab

我们把纸上打印出来的字符串从1开始顺序编号,一直到n。打字机有一个非常有趣的功能,在打字机中暗藏一个带数字的小键盘,在小键盘上输入两个数(x,y)(其中1≤x,y≤n),打字机会显示第x个打印的字符串在第y个打印的字符串中出现了多少次。

阿狸发现了这个功能以后很兴奋,他想写个程序完成同样的功能,你能帮助他么?

Input

输入的第一行包含一个字符串,按阿狸的输入顺序给出所有阿狸输入的字符。

第二行包含一个整数m,表示询问个数。

接下来m行描述所有由小键盘输入的询问。其中第i行包含两个整数x, y,表示第i个询问为(x, y)。

Output

输出m行,其中第i行包含一个整数,表示第i个询问的答案。

Sample Input

aPaPBbP

3

1 2

1 3

2 3

Sample Output

2

1

0

HINT

1<=N<=10^5

1<=M<=10^5

输入总长<=10^5

Source

Trie

[Submit][Status][Discuss]

sol::

一开始以为是sb题一道(后来确实是sb题,然而我这个sb卡了好久)

他的操作显然就是在构造一个ac自动机,考虑一个暴力,怎么检验x在y中出现了几次呢?就是让y在ac自动机上的每一个节点跳fail,如果一个节点跳到了x在ac自动机上的末尾节点,ans++。不妨建出一颗fail树。那么树上的儿子可以到达所有祖先的串(如果这个祖先被mark)。

对于ac自动机上的一个点,他在多个串中出现过,这些串显然是若干个区间,用vector进去出去的时候记一下就好了,显然这些区间最多有O(n)个。一开始的想法是使用一个权值线段树,然后就是区间+1的操作。这十分的naive啊。然后用线段树合并把儿子的信息合并到祖先上面,这样每个节点就可以知道有哪些串可以到达他。然后我这个蒟蒻就很naive的把线段树合并给可持久化了。想要做到询问Olog然后这显然空间是炸裂的。。

ps:和julao讨论了一下,好像单点修改的话可以暴力可持久化啊。然而区间加的话点数就爆炸了

这是一开始很naive的代码

#include<cstdio>
#include<algorithm>
#include<string>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<iostream>
#include<vector>
using namespace std;
int n,m;
inline int read()
{
char c;
int res,flag=0;
while((c=getchar())>'9'||c<'0') if(c=='-')flag=1;
res=c-'0';
while((c=getchar())>='0'&&c<='9') res=(res<<3)+(res<<1)+c-'0';
return flag?-res:res;
}
const int N=1e5+7;
int v=1,fa
,mark
;
int go
[26],p;
char sr
;
struct cc
{
int l,r;
};
vector<cc> q
;

namespace table
{
const int N=1e5+7;
int tot;
int nex
,go
,fir
,rt
;
inline void add(int x,int y)
{
nex[++tot]=fir[x];fir[x]=tot;go[tot]=y;
}
}

namespace tree
{
const int M=1e7+7;
int tot,rt
;
int sum[M],lc[M],rc[M];
inline void modify(int &k,int l,int r,int L,int R)
{
if(!k) k=++tot;
if(L>R) return;
if(L<=l&&r<=R)
{
sum[k]++;
return;
}
int mid=l+r>>1;
if(mid>=L) modify(lc[k],l,mid,L,R);
if(mid< R) modify(rc[k],mid+1,r,L,R);
}
inline int merge(int x,int y)
{
if(!x&&!y) return 0;
if(!x) return y;
if(!y) return x;
int z=++tot;
sum[z]=sum[x]+sum[y];
lc[z]=merge(lc[x],lc[y]);
rc[z]=merge(rc[x],rc[y]);
return z;
}
inline int query(int x,int l,int r,int pos)
{
if(!x) return 0;
if(l==r) return sum[x];
int mid=l+r>>1;
if(pos<=mid) return sum[x]+query(lc[x],l,mid,pos);
else return sum[x]+query(rc[x],mid+1,r,pos);
}
inline void dfs(int u,int f)
{
int e,v;
vector<cc>::iterator it;
for(it=q[u].begin();it!=q[u].end();++it)
modify(rt[u],1,p,(*it).l,(*it).r);

for(e=table::fir[u];v=table::go[e],e;e=table::nex[e])
if(v!=f)
{
dfs(v,u);
rt[u]=merge(rt[u],rt[v]);
}
}
}

namespace ac
{
int tot=1;
int fail
;
inline void fix(int &x)
{
if(!x) x=++tot;
}
int q
;
inline void build()
{
int u,v,t=0,w=1;
q[1]=1;
for(int i=0;i<=25;++i) go[0][i]=1;
while(t<w)
{
u=q[++t];
for(int i=0;i<=25;++i)
if(go[u][i])
{
q[++w]=go[u][i];
v=fail[u];
while(!go[v][i]) v=fail[v];
v=go[v][i];
fail[go[u][i]]=v;
}
}
for(int i=2;i<=tot;++i)
{
table::add(fail[i],i);
table::add(i,fail[i]);
}
}
}

int main()
{
//  freopen("2434.in","r",stdin);
//  freopen("2434.out","w",stdout);
scanf("%s",sr+1);
n=strlen(sr+1);
q[1].push_back((cc){1,0});
for(int i=1;i<=n;++i)
{
if(sr[i]=='P') mark[++p]=v;
else if(sr[i]=='B')
{
q[v][q[v].size()-1].r=p;
v=fa[v];
}
else
{
sr[i]-='a';
ac::fix(go[v][sr[i]]);
fa[go[v][sr[i]]]=v;
v=go[v][sr[i]];
q[v].push_back((cc){p+1,0});
}
}
while(v)
{
q[v][q[v].size()-1].r=p;
v=fa[v];
}

ac::build();
tree::dfs(1,1);

m=read();
for(int i=1;i<=m;++i)
{
int x,y;
x=read();
y=read();
int ans=tree::query(tree::rt[mark[x]],1,p,y);
printf("%d\n",ans);
}
}


那不能可持久化咋整啊。。我们考虑把询问离线一下直接挂在点上,这样就不用可持久化了,递归完子树直接合并上来就可以了(蠢就在这啊,一开始去看了下题解发现全是离线的,以为自己写的是在线的,结果自己突然要离线了)

十分naive的代码*2

#include<cstdio>
#include<algorithm>
#include<string>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<iostream>
#include<vector>
using namespace std;
int n,m;
inline int read()
{
char c;
int res,flag=0;
while((c=getchar())>'9'||c<'0') if(c=='-')flag=1;
res=c-'0';
while((c=getchar())>='0'&&c<='9') res=(res<<3)+(res<<1)+c-'0';
return flag?-res:res;
}
const int N=1e5+7;
int v=1,fa
,mark
;
int go
[26],p;
char sr
;
struct cc
{
int l,r;
};
vector<cc> q
,ask
;

namespace table
{
const int N=2e5+7;
int tot;
int nex
,go
,fir
,rt
;
inline void add(int x,int y)
{
nex[++tot]=fir[x];fir[x]=tot;go[tot]=y;
}
}
int ans
;
namespace tree
{
const int M=1e7+7;
int tot,rt
;
int sum[M],lc[M],rc[M];
inline void modify(int &k,int l,int r,int L,int R)
{
if(!k) k=++tot;
if(L>R) return;
if(L<=l&&r<=R)
{
sum[k]++;
return;
}
int mid=l+r>>1;
if(mid>=L) modify(lc[k],l,mid,L,R);
if(mid< R) modify(rc[k],mid+1,r,L,R);
}
inline int merge(int x,int y)
{
if(!x) return y;
if(!y) return x;
sum[x]=sum[x]+sum[y];
lc[x]=merge(lc[x],lc[y]);
rc[x]=merge(rc[x],rc[y]);
return x;
}
inline int query(int x,int l,int r,int pos)
{
if(!x) return 0;
if(l==r) return sum[x];
int mid=l+r>>1;
if(pos<=mid) return sum[x]+query(lc[x],l,mid,pos);
else return sum[x]+query(rc[x],mid+1,r,pos);
}
inline void dfs(int u,int f)
{
int e,v;
vector<cc>::iterator it;
for(it=q[u].begin();it!=q[u].end();++it)
modify(rt[u],1,p,(*it).l,(*it).r);

for(e=table::fir[u];v=table::go[e],e;e=table::nex[e])
if(v!=f)
{
dfs(v,u);
rt[u]=merge(rt[u],rt[v]);
}
for(it=ask[u].begin();it!=ask[u].end();++it)
ans[(*it).r]=query(rt[u],1,p,(*it).l);
}
}

namespace ac
{
int tot=1;
int fail
;
inline void fix(int &x)
{
if(!x) x=++tot;
}
int q
;
inline void build()
{
int u,v,t=0,w=1;
q[1]=1;
for(int i=0;i<=25;++i) go[0][i]=1;
while(t<w)
{
u=q[++t];
for(int i=0;i<=25;++i)
if(go[u][i])
{
q[++w]=go[u][i];
v=fail[u];
while(!go[v][i]) v=fail[v];
v=go[v][i];
fail[go[u][i]]=v;
}
}
for(int i=2;i<=tot;++i)
{
table::add(fail[i],i);
table::add(i,fail[i]);
}
}
}

int main()
{
//  freopen("2434.in","r",stdin);
//  freopen("2434.out","w",stdout);
scanf("%s",sr+1);
n=strlen(sr+1);
q[1].push_back((cc){1,0});
for(int i=1;i<=n;++i)
{
if(sr[i]=='P') mark[++p]=v;
else if(sr[i]=='B')
{
q[v][q[v].size()-1].r=p;
v=fa[v];
}
else
{
sr[i]-='a';
ac::fix(go[v][sr[i]]);
fa[go[v][sr[i]]]=v;
v=go[v][sr[i]];
q[v].push_back((cc){p+1,0});
}
}
while(v)
{
q[v][q[v].size()-1].r=p;
v=fa[v];
}

ac::build();

m=read();
for(int i=1;i<=m;++i)
{
int x,y;
x=read();
y=read();
//      int ans=tree::query(tree::rt[mark[x]],1,p,y);
//      printf("%d\n",ans);
ask[mark[x]].push_back((cc){y,i});
}
tree::dfs(1,1);
for(int i=1;i<=m;++i)
printf("%d\n",ans[i]);
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐