zoukankan      html  css  js  c++  java
  • [NOI2011]阿狸的打字机

    【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;
    }
    
    
  • 相关阅读:
    mysql存储过程笔记
    mysql 命令行操作
    第一天前来报到
    Android Studio导入工程版本问题、gradle版本问题
    LogUtils-定制自己的日志工具
    Git2.11安装下载和github使用
    Android Studio 2.3版本 以及相应的gradle3.3包
    android 导入第三方jar包和类库
    GifView——Android显示GIF动画
    Android中dip、dp、sp、pt和px的区别详解
  • 原文地址:https://www.cnblogs.com/hchhch233/p/10102173.html
Copyright © 2011-2022 走看看