BZOJ 2434: [Noi2011]阿狸的打字机 AC自动机+fail树+线段树

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个询问的答案。

这道题和前几天在 CF 上做的一道 G题几乎是相同的. 

不知道为什么那场比赛切掉 G 题的人会那么少~ 

回到本题,不难发现打字机给出的其实就是 trie 的形式. 

把 $trie$ 建出来后和 $fail$ 树一起跑,然后用线段树维护 dfs 序就好了. 

#include <queue> 
#include <cstdio> 
#include <cstring>
#include <vector> 
#include <algorithm>  
#define N 300003 
#define setIO(s) freopen(s".in","r",stdin)   
using namespace std;  
struct Seg  {  
	#define lson (now<<1) 
	#define rson (now<<1|1)  
	struct Node { 
		int sum; 
	}t[N<<2]; 
	void update(int l,int r,int now,int p,int v)  {  
		t[now].sum+=v; 
		if(l==r) return; 
		int mid=(l+r)>>1; 
		if(p<=mid) update(l,mid,lson,p,v); 
		else update(mid+1,r,rson,p,v); 
	} 
	int query(int l,int r,int now,int L,int R) {
		if(l>=L&&r<=R) return t[now].sum; 
		int mid=(l+r)>>1,re=0; 
		if(L<=mid) re+=query(l,mid,lson,L,R); 
		if(R>mid)  re+=query(mid+1,r,rson,L,R); 
		return re; 
	}
	#undef lson 
	#undef rson 
}seg; 
struct Node {   
	int ch[27],f,end;  
}t[N];
struct Ask { 
	int i,y; 
	Ask(int i=0,int y=0):i(i),y(y){}   
}; 
struct Graph { 
	int edges; 
	int hd[N],to[N],nex[N],fa[N],val[N]; 
	void addedge(int u,int v,int c) {
		nex[++edges]=hd[u],hd[u]=edges,to[edges]=v,val[edges]=c;   
	}  
}trie,tree; 
queue<int>q; 
vector<Ask>G[N];   
int n,m,tot,tim,id[N],que[N],answer[N],dfn[N],size[N]; 
char op[N]; 
void buildAC() {
	int i,j; 
	for(i=0;i<27;++i) if(t[0].ch[i]) q.push(t[0].ch[i]); 
	while(!q.empty()) {
		int u=q.front();q.pop(); 
		for(i=0;i<27;++i) {
			int p=t[u].ch[i]; 
			if(!p) {
				t[u].ch[i]=t[t[u].f].ch[i]; 
				continue;  
			} 
			t[p].f=t[t[u].f].ch[i]; 
			q.push(p);   
		}
	}
} 
void dfs(int u) { 
	dfn[u]=++tim, size[u]=1; 
	for(int i=tree.hd[u];i;i=tree.nex[i]) 
		dfs(tree.to[i]), size[u]+=size[tree.to[i]];     
}
void buildtree() { 
	for(int i=1;i<=tot;++i) tree.addedge(t[i].f, i,0);      
	dfs(0); 
}
void solve(int now,int x) {
	seg.update(1,tim,1,dfn[now],1);    
	for(int i=0;i<G[x].size();++i) 
		answer[G[x][i].i]=seg.query(1,tim,1,dfn[G[x][i].y],dfn[G[x][i].y]+size[G[x][i].y]-1);    
	for(int i=trie.hd[x];i;i=trie.nex[i]) {
		int v=trie.to[i],c=trie.val[i];  
		solve(t[now].ch[c], v);  
	}
	seg.update(1,tim,1,dfn[now],-1); 
} 
int main() {
	int i,j,lst=0,cc=0; 
	// setIO("input");              
	scanf("%s",op+1),n=strlen(op+1);
	for(i=1;i<=n;++i) {
		if(op[i]>='a'&&op[i]<='z') {     
			int c=op[i]-'a'; 
			if(!t[lst].ch[c]) {
				t[lst].ch[c]=++tot; 
				trie.addedge(lst,tot,c); 
				trie.fa[tot]=lst;   
			}
			id[i]=lst=t[lst].ch[c];              
		}
		else { 
			if(op[i]=='P') que[++cc]=i,id[i]=lst;    
			if(op[i]=='B') id[i]=lst=trie.fa[lst];        
		}          
	} 
	buildAC();
	buildtree();   
	scanf("%d",&m); 
	for(i=1;i<=m;++i) {
		int x,y; 
		scanf("%d%d",&x,&y); 
		x=id[que[x]],y=id[que[y]];     
		G[y].push_back(Ask(i,x));           
	} 
	solve(0,0);   
	for(i=1;i<=m;++i) printf("%d\n",answer[i]);         
	return 0; 
}

  

转载于:https://www.cnblogs.com/guangheli/p/11422738.html

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值