【NOI2011】阿狸的打字机Description
题目描述
打字机上只有28个按键,分别印有26个小写英文字母和'B'、'P'两个字母。经阿狸研究发现,这个打字机是这样工作的:
·输入小写字母,打字机的一个凹槽中会加入这个字母(这个字母加在凹槽的最后)。
·按一下印有'B'的按键,打字机凹槽中最后一个字母会消失。
·按一下印有'P'的按键,打字机会在纸上打印出凹槽中现有的所有字母并换行,但凹槽中的字母不会消失。
例如,阿狸输入aPaPBbP,纸上被打印的字符如下:
a aa ab
我们把纸上打印出来的字符串从1开始顺序编号,一直到n。打字机有一个非常有趣的功能,在打字机中暗藏一个带数字的小键盘,在小键盘上输入两个数(x,y)(其中1≤x,y≤n),打字机会显示第x个打印的字符串在第y个打印的字符串中出现了多少次。
阿狸发现了这个功能以后很兴奋,他想写个程序完成同样的功能,你能帮助他么?
输入输出格式
输入格式:
输入的第一行包含一个字符串,按阿狸的输入顺序给出所有阿狸输入的字符。
第二行包含一个整数m,表示询问个数。
接下来m行描述所有由小键盘输入的询问。其中第i行包含两个整数x, y,表示第i个询问为(x, y)。
输出格式:
输出m行,其中第i行包含一个整数,表示第i个询问的答案。
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
数据范围:
对于100%的数据,n<=100000,m<=100000,第一行总长度<=100000。
首先根据题意模拟我们就能建出一个AC自动机。
对于询问((x,y)),我们先将他们转化为其在AC自动机上对应节点,那么本质上询问的是(root-y)的路径上有多少节点通过(fail)指针转移可以到达(x)。
转化一个角度:通过(fail)指针转移可以到达(x)的节点等价于在(fail)树上(x)的子树中的节点。
然后我就不会了,我还是太菜了。。。
查询子树中的权值是一个经典的(dfs)序+树状数组的问题。询问到根路径的问题做法也一样。
但是这道题我们要同时维护(fail)树上的子树问题和字典树上的到根的路径问题,显然是不能同时用数据结构维护的(我不会)。所以我们考虑将其中一个问题暴力维护。
然后我们就考虑离线(就是这里没想到)。我们将所有询问按(y)排序,然后我们就在模拟打字机的过程,同时维护树状数组。当遇到(P)的时候就可以处理询问了。
总结:
遇到在线无法维护的问题,考虑是否可以通过离线来简化问题。
代码:
#include<bits/stdc++.h>
#define ll long long
#define N 100005
using namespace std;
inline int Get() {int x=0,f=1;char ch=getchar();while(ch<'0'||ch>'9') {if(ch=='-') f=-1;ch=getchar();}while('0'<=ch&&ch<='9') {x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}return x*f;}
char S[N];
int n,m;
int rt=1,cnt=1;
int pool[N],num;
int tot,id,ver[N],out;
struct Tire {
int ch[26],fail;
}tr[N<<2];
void build_fail() {
static queue<int>q;
q.push(rt);
int f;
while(!q.empty()) {
int v=q.front();
q.pop();
for(int i=0;i<26;i++) {
if(!tr[v].ch[i]) continue ;
int sn=tr[v].ch[i];
f=tr[v].fail;
while(f&&!tr[f].ch[i]) f=tr[f].fail;
if(!f) tr[sn].fail=1;
else tr[sn].fail=tr[f].ch[i];
q.push(sn);
}
}
}
struct load {
int to,next;
}s[N<<1];
int h[N],ecnt;
void add(int i,int j) {
s[++ecnt]=(load) {j,h[i]};h[i]=ecnt;
}
int dfn[N],ed[N],dfn_id;
void dfs(int v) {
dfn[v]=++dfn_id;
for(int i=h[v];i;i=s[i].next) {
int to=s[i].to;
dfs(to);
}
ed[v]=dfn_id;
}
struct query {
int x,y,id;
bool operator <(const query &a)const {
return y<a.y;
}
}q[N];
int ans[N];
struct BIT {
int low(int i) {return i&(-i);}
int w[N];
void add(int v,int f) {
if(!v) exit(-1);
for(int i=v;i<=cnt;i+=low(i)) w[i]+=f;
}
int query(int v) {
int ans=0;
for(int i=v;i;i-=low(i)) ans+=w[i];
return ans;
}
int query(int l,int r) {return query(r)-query(l-1);}
}bit;
int main() {
scanf("%s",S+1);
n=strlen(S+1);
m=Get();
int now=1;
pool[num=1]=1;
for(int i=1;i<=n;i++) {
if(S[i]=='B') {
now=pool[--num];
} else if(S[i]=='P') {
ver[++out]=now;
} else {
if(!tr[now].ch[S[i]-'a']) tr[now].ch[S[i]-'a']=++cnt;
now=pool[++num]=tr[now].ch[S[i]-'a'];
}
}
build_fail();
for(int i=2;i<=cnt;i++) add(tr[i].fail,i);
dfs(1);
for(int i=1;i<=m;i++) q[i].x=Get(),q[i].y=Get(),q[i].id=i;
sort(q+1,q+1+m);
int tag=1;
now=1;
num=0;
pool[num=1]=1;
for(int i=1;i<=n;i++) {
if(S[i]=='B') {
bit.add(dfn[pool[num]],-1);
now=pool[--num];
} else if(S[i]=='P') {
id++;
while(tag<=m&&q[tag].y==id) {
ans[q[tag].id]=bit.query(dfn[ver[q[tag].x]],ed[ver[q[tag].x]]);
tag++;
}
} else {
pool[++num]=now=tr[now].ch[S[i]-'a'];
bit.add(dfn[now],1);
}
}
for(int i=1;i<=m;i++) cout<<ans[i]<<"
";
return 0;
}