bzoj2434 fail树 + dfs序 + 树状数组
https://www.lydsy.com/JudgeOnline/problem.php?id=2434
- 打字机上只有28个按键,分别印有26个小写英文字母和'B'、'P'两个字母。经阿狸研究发现,这个打字机是这样工作的:
- ·输入小写字母,打字机的一个凹槽中会加入这个字母(这个字母加在凹槽的最后)。
- ·按一下印有'B'的按键,打字机凹槽中最后一个字母会消失。
- ·按一下印有'P'的按键,打字机会在纸上打印出凹槽中现有的所有字母并换行,但凹槽中的字母不会消失。
- 例如,阿狸输入aPaPBbP,纸上被打印的字符如下:
- a aa ab 我们把纸上打印出来的字符串从1开始顺序编号,一直到n。打字机有一个非常有趣的功能,在打字机中暗藏一个带数字的小键盘,在小键盘上输入两个数(x,y)(其中1≤x,y≤n),打字机会显示第x个打印的字符串在第y个打印的字符串中出现了多少次。
- 阿狸发现了这个功能以后很兴奋,他想写个程序完成同样的功能,你能帮助他么?
题意
首先第一个想法一定是把所有单词插入到AC自动机并且保存所有N个单词的end结点位置,对于x,y串的询问,将y串end结点到根节点之间的所有结点跳一边fail指针,将经过的结点全部 + 1,查询一下x的结点保存的大小即可。
当然这是纯暴力的写法,我们考虑去优化。
首先把对于同一y的x,当然可以合并起来一起处理
其次对于y串包含x串,就意味着x被包含的次数为fail树上将y全部结点加入之后的子树和。
对于这一类动态的去维护子树和的行为,我们自然而然的想到dfs序,求出整颗fail树上的dfs序之后用树状数组动态的对于每个询问加入y串和查询然后删除y串一气呵成。
到了这一步看似已经稳如狗,可交上去依旧TLE
事实上仔细看题意里给出的字符串构造方式,我们就会发现这些字符串并不是毫无关联的字符串,换言之,在开始的insert里面,不需要每次都插入一个独立的字符串,而是只需要开一个now来模拟当前结点的位置,如果遇到P则直接返回now,如果遇到B则将now变为他的父节点,插入时就变为它指向的目标子结点。
不仅仅如此,即使在最后离线查询的时候,事实上也是不需要每次都动态的加入整个y串和删除y串的
只要模拟当前字符串的移动路线,每次遇到B的时候返回父节点并且删除当前结点在dfs序中的加成,遇到插入的时候就在树状数组中相应位置 + 1,每次查询就不用遍历y结点到根节点的所有结点了。
- #include <map>
- #include <set>
- #include <ctime>
- #include <cmath>
- #include <queue>
- #include <stack>
- #include <vector>
- #include <string>
- #include <cstdio>
- #include <cstdlib>
- #include <cstring>
- #include <sstream>
- #include <iostream>
- #include <algorithm>
- #include <functional>
- using namespace std;
- inline int read(){int now=;register char c=getchar();for(;!isdigit(c);c=getchar());
- for(;isdigit(c);now=now*+c-'',c=getchar());return now;}
- #define For(i, x, y) for(int i=x;i<=y;i++)
- #define _For(i, x, y) for(int i=x;i>=y;i--)
- #define Mem(f, x) memset(f,x,sizeof(f))
- #define Sca(x) scanf("%d", &x)
- #define Sca2(x,y) scanf("%d%d",&x,&y)
- #define Sca3(x,y,z) scanf("%d%d%d",&x,&y,&z)
- #define Scl(x) scanf("%lld",&x);
- #define Pri(x) printf("%d\n", x)
- #define Prl(x) printf("%lld\n",x);
- #define CLR(u) for(int i=0;i<=N;i++)u[i].clear();
- #define LL long long
- #define ULL unsigned long long
- #define mp make_pair
- #define PII pair<int,int>
- #define PIL pair<int,long long>
- #define PLL pair<long long,long long>
- #define pb push_back
- #define fi first
- #define se second
- typedef vector<int> VI;
- const double eps = 1e-;
- const int maxn = 1e5 + ;
- const int maxm = 5e5 + ;
- const int INF = 0x3f3f3f3f;
- const int mod = 1e9 + ;
- int N,M,K;
- char mstr[maxn];
- char str[maxn];
- int Index[maxn];
- //链式前向星
- struct Edge{
- int to,next;
- }edge[maxm];
- int head[maxm],TOT;
- void init(int n){
- for(int i = ; i < n; i ++) head[i] = -;
- TOT = ;
- }
- void add(int u,int v){
- edge[TOT].to = v;
- edge[TOT].next = head[u];
- head[u] = TOT++;
- }
- //AC自动机
- int nxt[maxm][],fail[maxm],tot,root,fa[maxm];
- int newnode(int f){
- for(int i = ; i < ; i ++) nxt[tot][i] = -;
- fa[tot] = f;
- return tot++;
- }
- void init(){
- tot = ;root = newnode();
- }
- void Build(){
- queue<int>Q; init(tot);
- for(int i = ; i < ; i ++){
- if(~nxt[root][i]){
- fail[nxt[root][i]] = root;
- add(root,nxt[root][i]);
- Q.push(nxt[root][i]);
- }else{
- nxt[root][i] = root;
- }
- }
- while(!Q.empty()){
- int u = Q.front(); Q.pop();
- for(int i = ; i < ; i ++){
- if(~nxt[u][i]){
- fail[nxt[u][i]] = nxt[fail[u]][i];
- add(nxt[fail[u]][i],nxt[u][i]);
- Q.push(nxt[u][i]);
- }else{
- nxt[u][i] = nxt[fail[u]][i];
- }
- }
- }
- }
- //dfs序
- int num = ;
- PII pos[maxm];
- void dfs(int t,int la){
- pos[t].fi = ++num;
- for(int i = head[t]; ~i; i = edge[i].next){
- int v = edge[i].to;
- if(v == la) continue;
- dfs(v,t);
- }
- pos[t].se = ++num;
- }
- //树状数组
- int tree[maxm * ];
- void tadd(int x,int t){
- for(;x <= num; x += x & -x) tree[x] += t;
- }
- int getsum(int x){
- int s = ;
- for(;x > ;x -= x & -x) s += tree[x];
- return s;
- }
- struct Query{
- int x,y,id;
- }query[maxn];
- bool cmp(Query a,Query b){
- return a.y < b.y;
- }
- int ans[maxn];
- int main(){
- //freopen("C.in","r",stdin);
- scanf("%s",mstr);
- N = ;
- init();
- int now = ;
- for(int i = ; mstr[i]; i ++){
- if(mstr[i] == 'B'){
- now = fa[now];
- }else if(mstr[i] == 'P'){
- Index[++N] = now;
- }else{
- if(nxt[now][mstr[i] - 'a'] == -){
- nxt[now][mstr[i] - 'a'] = newnode(now);
- }
- now = nxt[now][mstr[i] - 'a'];
- }
- }
- Build();
- dfs(root,-);
- Sca(M);
- for(int i = ; i <= M ; i ++){
- query[i].x = read();
- query[i].y = read();
- query[i].id = i;
- }
- sort(query + ,query + + M,cmp);
- now = root;
- int cnt = ;
- int si = ;
- for(int i = ; mstr[i] && cnt <= M; i ++){
- if(mstr[i] == 'P'){
- si++;
- while(cnt <= M && query[cnt].y == si){
- ans[query[cnt].id] = getsum(pos[Index[query[cnt].x]].se) - getsum(pos[Index[query[cnt].x]].fi - );
- cnt++;
- }
- }else if(mstr[i] == 'B'){
- tadd(pos[now].fi,-);
- now = fa[now];
- }else{
- now = nxt[now][mstr[i] - 'a'];
- tadd(pos[now].fi,);
- }
- }
- for(int i = ; i <= M ; i ++){
- Pri(ans[i]);
- }
- return ;
- }
bzoj2434 fail树 + dfs序 + 树状数组的更多相关文章
- 2018.10.20 NOIP模拟 巧克力(trie树+dfs序+树状数组)
传送门 好题啊. 考虑前面的32分,直接维护后缀trietrietrie树就行了. 如果#号不在字符串首? 只需要维护第一个#前面的字符串和最后一个#后面的字符串. 分开用两棵trie树并且维护第一棵 ...
- 【BZOJ-2434】阿狸的打字机 AC自动机 + Fail树 + DFS序 + 树状数组
2434: [Noi2011]阿狸的打字机 Time Limit: 10 Sec Memory Limit: 256 MBSubmit: 2022 Solved: 1158[Submit][Sta ...
- 【Codeforces163E】e-Government AC自动机fail树 + DFS序 + 树状数组
E. e-Government time limit per test:1 second memory limit per test:256 megabytes input:standard inpu ...
- BZOJ 2434: [Noi2011]阿狸的打字机( AC自动机 + DFS序 + 树状数组 )
一个串a在b中出现, 那么a是b的某些前缀的后缀, 所以搞出AC自动机, 按fail反向建树, 然后查询(x, y)就是y的子树中有多少是x的前缀. 离线, 对AC自动机DFS一遍, 用dfs序+树状 ...
- 【bzoj3881】[Coci2015]Divljak AC自动机+树链的并+DFS序+树状数组
题目描述 Alice有n个字符串S_1,S_2...S_n,Bob有一个字符串集合T,一开始集合是空的. 接下来会发生q个操作,操作有两种形式: “1 P”,Bob往自己的集合里添加了一个字符串P. ...
- BZOJ_3881_[Coci2015]Divljak_AC自动机+dfs序+树状数组
BZOJ_3881_[Coci2015]Divljak_AC自动机+dfs序+树状数组 Description Alice有n个字符串S_1,S_2...S_n,Bob有一个字符串集合T,一开始集合是 ...
- NOI 2011 阿狸的打字机 (AC自动机+dfs序+树状数组)
题目大意:略(太长了不好描述) 良心LOJ传送门 先对所有被打印的字符串建一颗Trie树 观察数据范围,并不能每次打印都从头到尾暴力建树,而是每遍历到一个字符就在Trie上插入这个字符,然后记录每次打 ...
- HDU 3887:Counting Offspring(DFS序+树状数组)
http://acm.hdu.edu.cn/showproblem.php?pid=3887 题意:给出一个有根树,问对于每一个节点它的子树中有多少个节点的值是小于它的. 思路:这题和那道苹果树是一样 ...
- HDU 5293 Tree chain problem 树形dp+dfs序+树状数组+LCA
题目链接: http://acm.hdu.edu.cn/showproblem.php?pid=5293 题意: 给你一些链,每条链都有自己的价值,求不相交不重合的链能够组成的最大价值. 题解: 树形 ...
随机推荐
- 4.namespace
命名空间( namespace)是 Linux 内核的一个强大特性,为容器虚拟化的实现带来极大便 利. 利用这一特性,每个容器都可以拥有自己单独的命名空间,运行在其中的应用都像是在 独立的操作系统环境 ...
- HDU 1074 Doing Homework(经典状压dp)
题目链接 Doing Homework Ignatius has just come back school from the 30th ACM/ICPC. Now he has a ...
- Linux下git的使用——将已有项目放到github上
本地已经有一个项目了,需要将该项目放到github上,怎么操作? 步骤: 本地安装git,有github账号是前提. (1)先在github创建一个空的仓库,并复制链接地址.使用https,以.git ...
- 洛谷P1897电梯里的爱情题解
题目 这个题是一个搜索题,可以先算出最高楼层,并算出不重复的楼层的个数,要注意的一点就是一定不要把0楼算在内. 代码 #include<iostream> #include<cstr ...
- PHP获取网络图片并保存在本地目录
PHP获取网络图片并保存在本地目录思路: 代码如下: function file_exists_S3($url) { $state = @file_get_contents($url,0,null,0 ...
- mac 使用指南
资料检索: Command + Option + Esc 查看进程或关闭 深度开源为OPEN other 工具使用: Alfred快捷键:option+space iTerm2命令行工具 SSH Sh ...
- WC2019 tree
WC2019唯一一道正常的题,考场上没什么想法,也只拿到了暴力分.搞了一天终于做完了. 前置知识:purfer序,多项式exp或分治FTT. 对于\(type=0\)的,随便维护下,算下联通块即可. ...
- Hdoj 1203.I NEED A OFFER! 题解
Problem Description Speakless很早就想出国,现在他已经考完了所有需要的考试,准备了所有要准备的材料,于是,便需要去申请学校了.要申请国外的任何大学,你都要交纳一定的申请费用 ...
- Leetcode 75.颜色分类 By Python
给定一个包含红色.白色和蓝色,一共 n 个元素的数组,原地对它们进行排序,使得相同颜色的元素相邻,并按照红色.白色.蓝色顺序排列. 此题中,我们使用整数 0. 1 和 2 分别表示红色.白色和蓝色. ...
- Python3 与 C# 并发编程之~ 线程篇
2.线程篇¶ 在线预览:https://github.lesschina.com/python/base/concurrency/3.并发编程-线程篇.html 示例代码:https://gith ...