P2414 [NOI2011] 阿狸的打字机
P2414 [NOI2011] 阿狸的打字机
题目描述:
[NOI2011] 阿狸的打字机
题目描述
阿狸喜欢收藏各种稀奇古怪的东西,最近他淘到一台老式的打字机。打字机上只有 B
、P
两个字母。经阿狸研究发现,这个打字机是这样工作的:
- 输入小写字母,打字机的一个凹槽中会加入这个字母(这个字母加在凹槽的最后)。
- 按一下印有
B
的按键,打字机凹槽中最后一个字母会消失。 - 按一下印有
P
的按键,打字机会在纸上打印出凹槽中现有的所有字母并换行,但凹槽中的字母不会消失。
例如,阿狸输入 aPaPBbP
,纸上被打印的字符如下:
a aa ab
我们把纸上打印出来的字符串从
阿狸发现了这个功能以后很兴奋,他想写个程序完成同样的功能,你能帮助他么?
输入格式
输入的第一行包含一个字符串,按阿狸的输入顺序给出所有阿狸输入的字符。
第二行包含一个整数
接下来
输出格式
输出
数据范围
对于
-------------------------------------------------------------------------------------
根本不会字符串,所以我们先对询问重拳出击:
打字机会显示第 x个打印的字符串在第y个打印的字符串中出现了多少次
显然,可能有同一个y对应非常多个x,并且询问离线,所以我们可以 对每一次询问的y排序,将同一个y下所有的x一起统计答案
接下来我们考虑如何统计答案:
最暴力的办法当然是造一个AC自动机,对于每一个询问在AC自动机上跑一边.然而这样的时间复杂度并不优秀
根据大佬们的题解,AC自动机的 fail显然可以构成一颗树
我们考虑把这棵树建出来,(边的方向: t[x].fail->x)
建完树后我们会发现,之前从AC自动机的下往上跳,而现在是从上往下搜,这样的好处是:
你可以直接对所求y打上一个值为1标记,然后对于y下的每个x,从x开搜,搜索到的x子树下的权值即为询问(x,y)的答案
我们还能发现:
在一个子树内,dfn是连续的!
所以当我们想统计子树内的数据时,我们就可以用 线段树 或者 树状数组 这样 O(logn) 的数据结构来维护了
由于我又懒了我还想复习树状数组
所以这里选择用 树状数组 维护
Code
#include<bits/stdc++.h> const int N=2e5+5; using namespace std; int dfn_cnt,n,m,e_cnt,tot; int sum[N],head[N],dfn[N],low[N]; char c[N]; struct Edge{ int to,nxt; }e[N<<1]; void add(int x,int y) { e[++e_cnt]={y,head[x]}; head[x]=e_cnt; } struct que{ int x,y,id,ans; }q[N]; bool cmp1(que q1,que q2){return q1.y<q2.y;} bool cmp2(que q1,que q2){return q1.id<q2.id;} struct Trie{ int ch[26],ch_[26]; int fail,fa,id; }t[N]; int lowbit(int x){return x&-x;} void upd(int x,int val){while(x<=dfn_cnt)sum[x]+=val,x+=lowbit(x);} int query(int x){int res=0;while(x)res+=sum[x],x-=lowbit(x);return res;} int rt[N],ql[N],qr[N]; void get_fail() { queue<int> Q; for(int i=0;i<26;i++) { if(t[0].ch[i])Q.push(t[0].ch[i]); } while(!Q.empty()) { int u=Q.front();Q.pop(); for(int i=0;i<26;i++) { if(t[u].ch[i]) { t[t[u].ch[i]].fail=t[t[u].fail].ch[i]; Q.push(t[u].ch[i]); } else t[u].ch[i]=t[t[u].fail].ch[i]; } } } void get_dfn(int x) { dfn[x]=++dfn_cnt; for(int i=head[x];i;i=e[i].nxt) { int to=e[i].to; get_dfn(to); } low[x]=dfn_cnt; } void dfs(int x) { upd(dfn[x],1); if(t[x].id) { for(int i=ql[t[x].id];i<=qr[t[x].id];i++) { q[i].ans=query(low[rt[q[i].x]])-query(dfn[rt[q[i].x]]-1); } } for(int i=0;i<26;i++) { if(t[x].ch_[i]) { dfs(t[x].ch_[i]); } } upd(dfn[x],-1); } void work() { scanf("%s",c+1); int len=strlen(c+1);int now=0; //cout<<"len:"<<len<<"\n"; for(int i=1,cc;i<=len;i++) { if('a'<=c[i]&&c[i]<='z') { cc=c[i]-'a'; if(!t[now].ch[cc])t[now].ch[cc]=++tot,t[tot].fa=now; now=t[now].ch[cc]; } if(c[i]=='B')now=t[now].fa; if(c[i]=='P') { rt[++n]=now; t[now].id=n; } //cout<<now<<" "; } for(int i=0;i<=tot;i++) { for(int j=0;j<26;j++) { t[i].ch_[j]=t[i].ch[j]; } } cin>>m; get_fail(); for(int i=1;i<=tot;i++) { add(t[i].fail,i); } get_dfn(0); for(int i=1;i<=m;i++) { scanf("%d%d",&q[i].x,&q[i].y); q[i].id=i; } sort(q+1,q+1+m,cmp1); for(int i=1,now=1;i<=m;i=now) { ql[q[i].y]=i; while(q[i].y==q[now].y)now++; qr[q[i].y]=now-1; } dfs(0); sort(q+1,q+1+m,cmp2); for(int i=1;i<=m;i++) { printf("%d\n",q[i].ans); } } int main() { freopen("P2414.in","r",stdin);//freopen("P2414.out","w",stdout); work(); }