P2414 [NOI2011] 阿狸的打字机
P2414 [NOI2011] 阿狸的打字机
题目描述:
[NOI2011] 阿狸的打字机
题目描述
阿狸喜欢收藏各种稀奇古怪的东西,最近他淘到一台老式的打字机。打字机上只有 \(28\) 个按键,分别印有 \(26\) 个小写英文字母和 B、P 两个字母。经阿狸研究发现,这个打字机是这样工作的:
- 输入小写字母,打字机的一个凹槽中会加入这个字母(这个字母加在凹槽的最后)。
- 按一下印有
B的按键,打字机凹槽中最后一个字母会消失。 - 按一下印有
P的按键,打字机会在纸上打印出凹槽中现有的所有字母并换行,但凹槽中的字母不会消失。
例如,阿狸输入 aPaPBbP,纸上被打印的字符如下:
a
aa
ab
我们把纸上打印出来的字符串从 \(1\) 开始顺序编号,一直到 \(n\)。打字机有一个非常有趣的功能,在打字机中暗藏一个带数字的小键盘,在小键盘上输入两个数 \((x,y)\)(其中 \(1\leq x,y\leq n\)),打字机会显示第 \(x\) 个打印的字符串在第 \(y\) 个打印的字符串中出现了多少次。
阿狸发现了这个功能以后很兴奋,他想写个程序完成同样的功能,你能帮助他么?
输入格式
输入的第一行包含一个字符串,按阿狸的输入顺序给出所有阿狸输入的字符。
第二行包含一个整数 \(m\),表示询问个数。
接下来 \(m\) 行描述所有由小键盘输入的询问。其中第 \(i\) 行包含两个整数 \(x, y\),表示第 \(i\) 个询问为 \((x, y)\)。
输出格式
输出 \(m\) 行,其中第 \(i\) 行包含一个整数,表示第 \(i\) 个询问的答案。
数据范围
对于 \(100\%\) 的数据,\(1\leq n\leq 10^5\),\(1\leq m\leq10^5\),第一行总长度 \(\leq 10^5\)。
-------------------------------------------------------------------------------------
根本不会字符串,所以我们先对询问重拳出击:
打字机会显示第 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();
}

浙公网安备 33010602011771号