[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;
}

原文地址:https://www.cnblogs.com/hchhch233/p/10102173.html