zoukankan      html  css  js  c++  java
  • 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$变成了死循环。。。

    解决方案?$ an(frac{pi}{2}+kpi),kin 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
    ",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;
    }
    
  • 相关阅读:
    回顾初心
    团队作业6(代码复审+事后诸葛亮)
    Alpha阶段项目复审(鸽牌开发小分队)
    事后诸葛亮(鸽牌开发小分队)
    团队作业5
    第七篇Scrum冲刺博客
    第六篇Scrum冲刺博客
    第五篇Scrum冲刺博客
    第四篇Scrum冲刺博客
    第三篇Scrum冲刺博客
  • 原文地址:https://www.cnblogs.com/Yangrui-Blog/p/9427391.html
Copyright © 2011-2022 走看看