[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]
他的操作显然就是在构造一个ac自动机,考虑一个暴力,怎么检验x在y中出现了几次呢?就是让y在ac自动机上的每一个节点跳fail,如果一个节点跳到了x在ac自动机上的末尾节点,ans++。不妨建出一颗fail树。那么树上的儿子可以到达所有祖先的串(如果这个祖先被mark)。
对于ac自动机上的一个点,他在多个串中出现过,这些串显然是若干个区间,用vector进去出去的时候记一下就好了,显然这些区间最多有O(n)个。一开始的想法是使用一个权值线段树,然后就是区间+1的操作。这十分的naive啊。然后用线段树合并把儿子的信息合并到祖先上面,这样每个节点就可以知道有哪些串可以到达他。然后我这个蒟蒻就很naive的把线段树合并给可持久化了。想要做到询问Olog然后这显然空间是炸裂的。。
ps:和julao讨论了一下,好像单点修改的话可以暴力可持久化啊。然而区间加的话点数就爆炸了
这是一开始很naive的代码
那不能可持久化咋整啊。。我们考虑把询问离线一下直接挂在点上,这样就不用可持久化了,递归完子树直接合并上来就可以了(蠢就在这啊,一开始去看了下题解发现全是离线的,以为自己写的是在线的,结果自己突然要离线了)
十分naive的代码*2
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]); }
相关文章推荐
- BZOJ2434 [Noi2011]阿狸的打字机(AC自动机 + fail树 + DFS序 + 线段树)
- [AC自动机 fail树 树状数组] BZOJ 2434 [NOI2011] 阿狸的打字机
- BZOJ 2434: [Noi2011]阿狸的打字机( AC自动机 + DFS序 + 树状数组 )
- BZOJ.2434.[NOI2011]阿狸的打字机(AC自动机 树状数组 DFS序)
- bzoj2434(NOI2011).阿狸的打字机(AC自动机 && DFS序 && 树状数组)
- 【AC自动机】 BZOJ 2434 [Noi2011]阿狸的打字机
- [BZOJ2434]NOI2011阿狸的打字机|AC自动机|fail树|树状数组
- BZOJ 2434: [Noi2011]阿狸的打字机 AC自动机 fail树
- BZOJ_2434_[Noi2011]阿狸的打字机_AC自动机+出栈入栈序+树状数组
- bzoj 2434 (NOI2011)阿狸的打字机 【AC自动机】【树状数组】【DFS序】
- 【BZOJ2434-[Noi2011]】阿狸的打字机(AC自动机(fail树)+离线+树状数组)
- BZOJ2434 [Noi2011]阿狸的打字机 【AC自动机 + fail树 + 树状数组】
- bzoj2434 [Noi2011]阿狸的打字机 ( AC自动机 & fail树 + 树状数组 + dfs序 )
- BZOJ2434 [Noi2011]阿狸的打字机 【AC自动机 + fail树 + 树状数组】
- BZOJ 2434([Noi2011]阿狸的打字机-AC自动机-Fail树)
- [BZOJ2434][NOI2011]阿狸的打字机-AC自动机
- 【BZOJ2434】【NOI2011】阿狸的打字机 AC自动机
- BZOJ 2434: [Noi2011]阿狸的打字机【AC自动机,fail树.dfs序,树状数组
- BZOJ 2434 [Noi2011]阿狸的打字机(AC自动机)
- BZOJ 2434: [Noi2011]阿狸的打字机 [AC自动机 Fail树 树状数组 DFS序]