[BZOJ2746]-[HEOI2012]旅行问题-fail树+倍增LCA
来源:互联网 发布:简单菜谱软件下载 编辑:程序博客网 时间:2024/05/21 10:19
说在前面
感觉这个题面实在是…不清晰
题目
BZOJ2746传送门
题目大意
给定n个字符串,共有m次询问,每次询问输入四个数S1,L1,S2,L2,表示求「第S1个字符串长度为L1的前缀」和「第S2个字符串长度为L2的前缀」的最长公共后缀,且要求这个最长公共后缀是给定的某一个串的前缀。
为了不使输出过大,你只需把这个字符串生成的26进制数转成10进制后mod 1000000007输出。
输入输出格式
输入格式:
第一行给定一个整数n
第2…n+1行:每i+1行给定一个字符串a[i]。
接下来一行一个整数m
接下来m行:每行给定四个整数S1,L1,S2,L2,字母含义与题目描述一致。
输出格式:
共m行,每行一个整数,表示答案字符串的编码
解法
题目保证了最多会有1e6个不相同的前缀,因此可以建下一棵trie
建完trie之后把AC自动机搞出来,建立fail树。根据定义,询问的这个节点就是在fail树上的
然而有一个很奇怪的地方,它的询问是S1的第L1个字符,那岂不是要把所有字符对应的编号全部都存下来。这样的话,根据他保证的输入不超过20M,那输入最多会有两千万个字符,再加上倍增所用的fa[20][1000005],再加上存每个节点的child的空间(节点数*26),就已经超过256M了= =
当然解决办法也比较简单,还必须得先把输入的字符串存下来(输入小于20M,可以存)。然后读入所有询问,这时候再去建立trie,只记录下询问中的要用的那些节点的编号,才能避免炸空间的问题= =…
(不过真不知道考场上卡这个有什么意思…当然BZOJ数据貌似不到两百万个字符,所以可以直接存编号)
下面是自带大常数的代码
#include <queue>#include <cstdio>#include <cstring>#include <algorithm>using namespace std ;const int mmod = 1000000007 ;int N , AC_cnt , head[1000005] , M , tp ;int memid[2000005] , st[1000005] , topp ;char ss[1000005] ; struct Node{ int val , id ; Node *fail , *ch[26] ;}*root , *arc[1000005] ;struct Path{ int pre , to ;}p[1000005] ;void In( int t1 , int t2 ){ p[++tp].pre = head[t1] ; p[ head[t1] = tp ].to = t2 ;}int dep[1000005] , fa[20][1000005] ;void dfs( const int &u ){ for( int i = head[u] ; i ; i = p[i].pre ){ int v = p[i].to ; dep[v] = dep[u] + 1 ; fa[0][v] = u ; dfs( v ) ; }}void getST(){ register int i , j ; for( i = 1 ; i <= 19 ; i ++ ) for( j = 1 ; j <= AC_cnt ; j ++ ) fa[i][j] = fa[i-1][ fa[i-1][j] ] ;}int Lca( int u , int v ){ if( dep[u] < dep[v] ) swap( u , v ) ; int t = dep[u] - dep[v] , x = 0 ; while( t ){ if( t&1 ) u = fa[x][u] ; t >>= 1 ; x ++ ; } if( u == v ) return u ; for( int i = 19 ; i >= 0 ; i -- ) if( fa[i][u] != fa[i][v] ) u = fa[i][u] , v = fa[i][v] ; return fa[0][u] ;}void newNode( Node *&nd , int val_ ){ nd = new Node() ; nd->id = ++AC_cnt , arc[AC_cnt] = nd ; nd->val = val_ ; nd->fail = NULL ; memset( nd->ch , 0 , sizeof( nd->ch ) ) ;}void Insert( char *ts , int i_th ){ int len = strlen( ts ) ; Node *nd = root ; st[i_th] = topp ; for( int i = 0 ; i < len ; i ++ ){ int nxt = ts[i] - 'a' ; if( !nd->ch[nxt] ) newNode( nd->ch[nxt] , ( 26LL * nd->val + nxt )%mmod ) ; nd = nd->ch[nxt] ; memid[++topp] = nd->id ; }}queue<Node*> que ;void getFail(){ que.push( root ) ; while( !que.empty() ){ Node *u = que.front() ; que.pop() ; for( int i = 0 ; i < 26 ; i ++ ){ if( !u->ch[i] ) continue ; Node *p = u->fail , *v = u->ch[i] ; while( p && !p->ch[i] ) p = p->fail ; v->fail = ( p ? p->ch[i] : root ) ; In( v->fail->id , v->id ) ; que.push( v ) ; } }}void solve(){ getFail() ; fa[0][1] = 1 ; dfs( 1 ) ; getST() ; scanf( "%d" , &M ) ; for( int i = 1 ; i <= M ; i ++ ){ int s1 , l1 , s2 , l2 ; scanf( "%d%d%d%d" , &s1 , &l1 , &s2 , &l2 ) ; int tmp = Lca( memid[ st[s1] + l1 ] , memid[ st[s2] + l2 ] ) ; printf( "%d\n" , arc[tmp]->val ) ; }}int main(){ newNode( root , 0 ) ; scanf( "%d" , &N ) ; for( int i = 1 ; i <= N ; i ++ ){ scanf( "%s" , ss ) ; Insert( ss , i ) ; } solve() ;}
- [BZOJ2746]-[HEOI2012]旅行问题-fail树+倍增LCA
- bzoj2746 [HEOI2012]旅行问题 ( AC自动机 & fail树 +lca + hash )
- bzoj2746 旅行问题
- BZOJ 2746 旅行问题 (fail树 Hash LCA)
- HEOI 2012 旅行问题 BZOJ2746
- 2746: [HEOI2012]旅行问题
- 倍增/树链剖分解决LCA问题
- LCA 问题的倍增解法
- 最大生成树+LCA倍增
- lca倍增
- lca(倍增)
- 倍增LCA
- 倍增lca
- LCA倍增
- LCA(倍增)
- 倍增 LCA
- [模板]用倍增求LCA问题
- 【LCA】倍增法 LCA
- MyBatis批量处理Oracle数据库数据
- Java面试题全集(下)
- Longest Valid Parentheses:最长括号子段匹配
- 红米1S的android 4.4.4刷机到android 7.1的Lineage OS 14.1
- idea 创建Web Service Client 报错 java.lang.AssertionError: org.xml.sax.SAXParseException; systemId:
- [BZOJ2746]-[HEOI2012]旅行问题-fail树+倍增LCA
- 算法分析与设计丨第十五周丨LeetCode(19)——Longest Palindromic Substring(Medium)
- Android动画插值器之PathInterpolator浅析
- spring-session使用配置(分布式共享session配置)
- 剑指offer — 翻转单词
- 贪吃蛇大作战单人版完整版代码以及详解
- 科研笔记
- 互联网时代的营销方法
- Java学习