BZOJ 4556 [HEOI2016/TJOI2016]字符串
BZOJ 4556 [HEOI2016/TJOI2016]字符串
其实题解更多是用后缀数组+数据结构的做法,貌似也不好写。
反正才学了 sam 貌似比较简单的做法。
还是得先二分,然后倍增跳到 $ s[c...c+mid-1] $ 所在的节点,然后看看有没有 endpos 在 $ a+mid-1...b $ 内就好了。
复杂度是二分和倍增的 $ nlog^2n $。
其实这道题因为只用求 endpos 是否存在啥的 vector + lower_bound 貌似都可以过了。。但其实启发式合并也不好写,还是写一下线段树合并吧,有些时候会要求维护一些其他的信息,而且复杂度只在最初合并有区别,并不是复杂度瓶颈。
开始线段树写成 On T了半天海星。。
#include "iostream"
#include "algorithm"
#include "cstring"
#include "cstdio"
#include "ctime"
using namespace std;
#define MAXN 200006
int n , m , ps;
char ch[MAXN];
namespace solve {
int T[MAXN*20] , ls[MAXN*20] , rs[MAXN*20] , rt[MAXN] , cn;
void add( int& rt , int l , int r , int p ) {
if( !rt ) rt = ++ cn;
++ T[rt];
if( l == r ) return;
int m = l + r >> 1;
if( p <= m ) add( ls[rt] , l , m , p );
else add( rs[rt] , m + 1 , r , p );
}
int merge( int u , int v , int l , int r ) {
if( !u || !v ) return u ^ v;
int cur = ++ cn , m = l + r >> 1;
T[cur] = T[u] + T[v];
if( l == r ) return cur;
ls[cur] = merge( ls[u] , ls[v] , l , m );
rs[cur] = merge( rs[u] , rs[v] , m + 1 , r );
return cur;
}
int que( int rt , int l , int r , int L , int R ) {
if( !rt ) return 0;
if( L <= l && R >= r ) return T[rt];
int m = l + r >> 1 , res = 0;
if( L <= m ) res += que( ls[rt] , l , m , L , R );
if( R > m ) res += que( rs[rt] , m + 1 , r , L , R );
return res;
}
int son[MAXN][26] , len[MAXN] , par[MAXN] , lst[MAXN];
int last , cnt;
int head[MAXN] , to[MAXN] , nex[MAXN] , ecn;
void ade( int u , int v ) {
to[++ ecn] = v , nex[ecn] = head[u] , head[u] = ecn;
}
void addall( ) {
for( int i = 2 ; i <= cnt ; ++ i ) ade( par[i] , i );
}
void init( ) {
cnt = last = 1;
}
void ins( int c ) {
int cur = ++ cnt;
len[cur] = len[last] + 1;
int p = last;
while( p && !son[p][c] ) son[p][c] = cur , p = par[p];
if( !p ) par[cur] = 1;
else {
int q = son[p][c];
if( len[q] == len[p] + 1 ) par[cur] = q;
else {
int cl = ++ cnt;
memcpy( son[cl] , son[q] , sizeof son[q] );
par[cl] = par[q] , len[cl] = len[p] + 1;
par[q] = par[cur] = cl;
while( p ) { if( son[p][c] == q ) son[p][c] = cl; p = par[p]; }
}
}
last = cur , lst[++ ps] = cur;
add( rt[cur] , 1 , n , ps );
}
int G[MAXN][19];
int work( int u , int ln ) {
for( int k = 18 ; k >= 0 ; -- k )
if( len[G[u][k]] >= ln ) u = G[u][k];
return u;
}
void dfs( int u , int fa ) {
for( int i = head[u] ; i ; i = nex[i] ) {
int v = to[i];
if( v == fa ) continue;
G[v][0] = u;
for( int k = 1 ; k < 19 ; ++ k )
if( G[G[v][k-1]][k-1] ) G[v][k] = G[G[v][k-1]][k-1];
else break;
dfs( v , u );
rt[u] = merge( rt[u] , rt[v] , 1 , n );
}
}
}
int main() {
//freopen("7.in","r",stdin);
//freopen("ot","w",stdout);
cin >> n >> m;
scanf("%s",ch + 1);
using namespace solve;
init( );
for( int i = 1 ; i <= n ; ++ i )
ins( ch[i] - 'a' );
addall( );
dfs( 1 , 1 );
for( int i = 1 , a , b , c , d ; i <= m ; ++ i ) {
scanf("%d%d%d%d",&a,&b,&c,&d);
int l = 1 , r = d - c + 1;
while( l <= r ) {
int mid = l + r >> 1;
int t = work( lst[c + mid - 1] , mid );
if( que( rt[t] , 1 , n , a + mid - 1 , b ) ) l = mid + 1;
else r = mid - 1;
}
printf("%d\n",r);
}
}
BZOJ 4556 [HEOI2016/TJOI2016]字符串的更多相关文章
- 【BZOJ 4556】[Tjoi2016&Heoi2016]字符串 SAM+二分+主席树
这道题市面上就两种法:一种是SA+二分+主席树,一种是SAM+二分+主席树(有不少人打线段树合并???)(除此之外还有一种利用炒鸡水的数据的暴力SA,贼快.....)(当时学SA的时候没做这道题,现在 ...
- BZOJ.4553.[HEOI2016&TJOI2016]序列(DP 树状数组套线段树/二维线段树(MLE) 动态开点)
题目链接:BZOJ 洛谷 \(O(n^2)\)DP很好写,对于当前的i从之前满足条件的j中选一个最大值,\(dp[i]=d[j]+1\) for(int j=1; j<i; ++j) if(a[ ...
- P4094 [HEOI2016/TJOI2016]字符串 后缀数组+主席树+二分答案
$ \color{#0066ff}{ 题目描述 }$ 佳媛姐姐过生日的时候,她的小伙伴从某东上买了一个生日礼物.生日礼物放在一个神奇的箱子中.箱子外边写了一个长为n的字符串s,和m个问题.佳媛姐姐必须 ...
- [HEOI2016/TJOI2016]字符串
嘟嘟嘟 今天复习一下SAM. lcp固然不好做,干脆直接翻过来变成后缀.首先答案一定满足单调性,所以我们二分lcp的长度\(mid\),然后判断\(s[d \ldots d + mid - 1]\)是 ...
- BZOJ.4555.[HEOI2016&TJOI2016]求和(NTT 斯特林数)
题目链接 \(Description\) 求\[\sum_{i=0}^n\sum_{j=0}^iS(i,j)\times 2^j\times j!\mod 998244353\] 其中\(S(i,j) ...
- [HEOI2016/TJOI2016]字符串(后缀数组+二分+主席树/后缀自动机+倍增+线段树合并)
后缀数组解法: 先二分最长前缀长度 \(len\),然后从 \(rnk[c]\) 向左右二分 \(l\) 和 \(r\) 使 \([l,r]\) 的 \(height\geq len\),然后在主席树 ...
- 洛谷 P4093: bzoj 4553: [HEOI2016/TJOI2016]序列
题目传送门:洛谷P4093. 题意简述: 给定一个长度为 \(n\) 的序列 \(a\). 同时这个序列还可能发生变化,每一种变化 \((x_i,y_i)\) 对应着 \(a_{x_i}\) 可能变成 ...
- BZOJ.4552.[HEOI2016/TJOI2016]排序(线段树合并/二分 线段树)
题目链接 对于序列上每一段连续区间的数我们都可以动态开点建一棵值域线段树.初始时就是\(n\)棵. 对于每次操作,我们可以将\([l,r]\)的数分别从之前它所属的若干段区间中分离出来,合并. 对于升 ...
- 【[HEOI2016/TJOI2016]字符串】
码农题啊 上来先无脑一个\(SA\)的板子,求出\(SA\)和\(het\)数组 我们只需要从\(sa[i]\in[a,b]\)的所有\(i\)中找到一个\(i\)使得\(sa[i]\)和\(rk[c ...
随机推荐
- 《JavaScript DOM编程艺术》:+= 相加之后再赋值
第2章 第20页 += var year = 2010; var message = "The year is"; message += year; message += yea ...
- cassandra表中主键的类型
cassandra表中主键的类型及区分? 一.类型及区分 二.参考文章 一.类型及区分 Cassandra的4种Key Primary Key 主键 Composite Key,Compound Ke ...
- Scrum Meeting 0531
零.说明 日期:2021-5-31 任务:简要汇报两日内已完成任务,计划后两日完成任务 一.进度情况 组员 负责 两日内已完成的任务 后两日计划完成的任务 困难 qsy PM&前端 完成后端管 ...
- [no_code]团队任务拆解Alpha
项目 内容 这个作业属于哪个课程 2020春季计算机学院软件工程(罗杰 任健) 这个作业的要求在哪里 团队任务拆解 我们在这个课程的目标是 远程协同工作,采用最新技术开发软件 这个作业在哪个具体方面帮 ...
- Manjaro / ArchLinux 安装网易云音乐解决搜索不能输入中文方法
0. 安装网易云音乐 yay -S netease-cloud-music 1.先安装qcef这个软件包. sudo yay -S qcef 2.编辑/opt/netease/netease-clou ...
- 基于openeuler的openssl编程
------------恢复内容开始------------ 一.编译环境 我下载好之后默认安装了openssl,若未安装的可输入以下命令: wget https://www.openssl.org/ ...
- hdu 4288 Coder (线段树+离线)
题意: 刚开始有一个空集合.有三种操作: 1.往集合中加入一个集合中不存在的数 x 2.从集合中删除一个已经存在的数 x 3.计算集合的digest sum并输出. digest sum求 ...
- pycharm基本使用python的注释语法
pychram基本使用 1.主题选择 file settings Editor color Scheme 2.pycharm切换解释器 file settings Project Python Int ...
- Java线程的三种实现方法
Java多线程详解 线程简介 多任务,多线程 多任务情况中,虽然可以完成,但是实际上,多任务的完成是由一个一个小任务的完成来实现的,也就是说在执行多任务时,不是同时执行多个任务,而是一个时间段内只完成 ...
- selenium2.x 与 selenium3.x 最大区别
一.selenium2.x 与 selenium3.x 最大区别 (1) 从3.0版本selenium开始使用火狐浏览器完成web自动化就需要用到驱动包了. (2) 而2.0版本的selenium使用 ...