BZOJ2434: [Noi2011]阿狸的打字机

BZOJ2434: [Noi2011]阿狸的打字机

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

 

题解Here!

这题有毒!这题有毒!!这题有毒!!!

先说一说正解:

AC自动机没得说。。。

将插入操作改成在线处理,不然你会TLE的。

然后,画出一个AC自动机的简图,你会发现一个有$(keng)$趣$(die)$的结论:

对于AC自动机中的每一个节点,如果节点A的$fail$指向节点B,那么B对应的字符串一定在A对应的字符串中出现!

于是,所有的询问变成了:

有多少个属于Y的节点的$fail$指针 _直接或间接_ 地指向X的结束位置。

然而还是会TLE。。。

再回看我们画的图,会发现:

所有的$fail$与原图中的节点构成了一颗树!

(我们一般称它为$fail$树。)

现在询问变成了:

在$fail$树中,以X结束点为根的子树中,有多少个点属于Y。

当然可以用树剖。。。

但是这里用树上差分。

每到一个点,我们就让它对应的序列点+1,回溯的时候-1。

这样做可以保证只有当前路径上的点是有值的。

如果当前点是一个结束节点,那么说明每一个属于这个串的节点都已统计完毕。

此时我们只需调出每一个关于这个串的询问,区间求和即可。

这玩意可以丢给树状数组,就不用第三遍dfs了。

处理询问的时候,离线将$y$从小到大排个序,求出左右端点,树状数组求个和即可。

然后就是乱七八槽的一大堆映射+变量名。。。

再讲一讲代码:

我调了一下午,然后发现一个巨大的问题:

AC自动机在建立$fail$指针的时候,会把$son$一并改掉。。。

然后你会发现你的遍历$Trie$树的$DFS$变成了死循环。。。

解决方案?$\tan(\frac{\pi}{2}+k\pi),k\in Z$

所以,再新建一个数组保存原来的$Trie$树吧。。。

附代码:

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
#include<queue>
#define MAXN 200010
using namespace std;
int n=0,m,size=1,d=1,e=0;
int ans[MAXN],head[MAXN],id[MAXN],low[MAXN],bst[MAXN];
int que_left[MAXN],que_right[MAXN],word[MAXN];
bool vis[MAXN];
struct AC{
	int fail,val,fa,son[26],trie_son[26];
	AC(){
		fail=val=fa=0;
		memset(son,0,sizeof(son));
	}
}a[MAXN];
struct Fail_Tree{
	int next,to;
}b[MAXN<<1];
struct Question{
	int x,y,id;
}que[MAXN];
inline int read(){
	int date=0,w=1;char c=0;
	while(c<'0'||c>'9'){if(c=='-')w=-1;c=getchar();}
	while(c>='0'&&c<='9'){date=date*10+c-'0';c=getchar();}
	return date*w;
}
inline bool cmp(const Question &p,const Question &q){
	return p.y<q.y;
}
inline int idx(char c){return c-'a';}
void buildtree(){
	int u,v;
	queue<int> q;
	for(int i=0;i<26;i++)
	if(a[0].son[i]){
		a[a[0].son[i]].fail=0;
		q.push(a[0].son[i]);
	}
	while(!q.empty()){
		u=q.front();
		q.pop();
		for(int i=0;i<26;i++){
			if(a[u].son[i]){
				a[a[u].son[i]].fail=a[a[u].fail].son[i];
				q.push(a[u].son[i]);
			}
			else
			a[u].son[i]=a[a[u].fail].son[i];
		}
	}
}
inline int lowbit(int x){return x&(-x);}
inline void update(int x,int v){
	for(int i=x;i<=e;i+=lowbit(i))bst[i]+=v;
}
inline int sum(int x){
	int s=0;
	for(int i=x;i;i-=lowbit(i))s+=bst[i];
	return s;
}
inline void add(int x,int y){
	b[d].to=y;b[d].next=head[x];head[x]=d++;
}
void dfs_fail(int rt){
    id[rt]=++e;
    for(int i=head[rt];i;i=b[i].next){
    	int will=b[i].to;
    	dfs_fail(will);
    }
    low[rt]=e;
}
void dfs_AC(int u){
    update(id[u],1);
    if(a[u].val)
    for(int i=que_left[a[u].val];i<=que_right[a[u].val];i++)
    ans[que[i].id]=sum(low[word[que[i].x]])-sum(id[word[que[i].x]]-1);
    for(int i=0;i<26;i++)if(a[u].trie_son[i])dfs_AC(a[u].trie_son[i]);
    update(id[u],-1);
}
void work(){
	m=read();
	for(int i=1;i<=m;i++){
		que[i].x=read();que[i].y=read();
		que[i].id=i;
	}
	sort(que+1,que+m+1,cmp);
	for(int i=1;i<size;i++)add(a[i].fail,i);
	dfs_fail(0);
	for(int i=1,j=1;i<=m;i=j){
		que_left[que[i].y]=i;
		while(que[j].y==que[i].y)j++;
		que_right[que[i].y]=j-1;
	}
	dfs_AC(0);
	for(int i=1;i<=m;i++)printf("%d\n",ans[i]);
}
void init(){
    int len;
	char ch[MAXN>>1];
	scanf("%s",ch);
	len=strlen(ch);
	for(int i=0,u=0;i<len;i++){
		if(u==0&&ch[i]=='P')continue;
		int c=idx(ch[i]);
		switch(ch[i]){
			case 'P':{word[++n]=u;a[u].val=n;break;}
			case 'B':{u=a[u].fa;break;}
			default:{
				if(!a[u].son[c]){
					a[u].son[c]=a[u].trie_son[c]=size++;
					a[a[u].son[c]].fa=u;
				}
				u=a[u].son[c];
				break;
			}
		}
	}
	buildtree();
}
int main(){
	init();
	work();
	return 0;
}

 

posted @ 2018-08-05 21:38  符拉迪沃斯托克  阅读(143)  评论(0编辑  收藏  举报
Live2D