题目传送门

Description

给出一个长度为 \(n\) 的 \(01\) 串为 \(s\),设 \(t_i\) 为 \(s_{1,2,..,i}\),有 \(m\) 次查询,每次查询给出 \(l,r\),求 \([l,r]\) 之间 \(t_i\) 的最长公共后缀长度的最大值。

\(n,m\le 10^5\)

Solution

本来不想写题解的,但想了想还是写一下吧。

不难想到,假设 \(f_i\) 为 \(t_i\) 在后缀自动机上对应的点,那么就相当于查询:

\[\max\{\text{deep}(\text{lca}(f_i,f_j))\},l\le i,j\le r
\]

考虑到在线不好搞,所以我们离线下来。然后你发现按左端点排序根本就不好做(不要问我怎么知道的),然后你发现按右端点排序之后就好做了。

我们可以在加入一个点的时候把 \(\text{parent}\) 树上都标记一下,那么如果过程中对于点 \(u\),如果已经标记过 \(i\) 了,那么说明对于当前右端点,左端点 \(\le i\) 的区间 \(u\) 都可以产生贡献,就可以树状数组修改一下。由此我们也可以看出,我们需要保存的是最新标记的标记。

复杂度显然是 \(\Theta(n\log^2n)\) 。

Code

#include <bits/stdc++.h>
using namespace std; #define Int register int
#define MAXN 200005 template <typename T> inline void read (T &t){t = 0;char c = getchar();int f = 1;while (c < '0' || c > '9'){if (c == '-') f = -f;c = getchar();}while (c >= '0' && c <= '9'){t = (t << 3) + (t << 1) + c - '0';c = getchar();} t *= f;}
template <typename T,typename ... Args> inline void read (T &t,Args&... args){read (t);read (args...);}
template <typename T> inline void write (T x){if (x < 0){x = -x;putchar ('-');}if (x > 9) write (x / 10);putchar (x % 10 + '0');} char s[MAXN];
int n,m,ans[MAXN]; struct Bit_Tree{
int maxn[MAXN];
int lowbit (int x){return x & (-x);}
void modify (int x,int v){x = n - x + 1;for (Int i = x;i <= n;i += lowbit (i)) maxn[i] = max (maxn[i],v);}
int query (int x){x = n - x + 1;int ans = 0;for (Int i = x;i;i -= lowbit (i)) ans = max (ans,maxn[i]);return ans;}
}T1; struct node{
int ind,qL,qR;
bool operator < (const node &p)const{return qR < p.qR;}
};
vector <node> S[MAXN]; int lst = 1,cnt = 1,ch[MAXN][2],len[MAXN],git[MAXN],fail[MAXN]; void extend (int c){
int f = lst,q = ++ cnt;lst = q,len[q] = len[f] + 1;
while (f && !ch[f][c]) ch[f][c] = q,f = fail[f];
if (!f) fail[q] = 1;
else{
int x = ch[f][c];
if (len[x] == len[f] + 1) fail[q] = x;
else{
int p = ++ cnt;
fail[p] = fail[x],len[p] = len[f] + 1,memcpy (ch[p],ch[x],sizeof (ch[p]));
fail[x] = fail[q] = p;while (f && ch[f][c] == x) ch[f][c] = p,f = fail[f];
}
}
} struct LCT{
int id[MAXN],fa[MAXN],son[MAXN][2];
bool rnk (int x){return son[fa[x]][1] == x;}
bool Isroot (int x){return son[fa[x]][rnk(x)] != x;}
void rotate (int x){
int y = fa[x],z = fa[y],k = rnk(x),w = son[x][!k];
if (!Isroot (y)) son[z][rnk(y)] = x;son[x][!k] = y,son[y][k] = w;
if (w) fa[w] = y;fa[x] = z,fa[y] = x;
}
void Pushdown (int x){
if (son[x][0]) id[son[x][0]] = id[x];
if (son[x][1]) id[son[x][1]] = id[x];
}
void Pushall (int x){
if (!Isroot (x)) Pushall (fa[x]);
Pushdown (x);
}
void Splay (int x){
Pushall (x);
while (!Isroot (x)){
int y = fa[x];
if (!Isroot (y)) rotate (rnk(x) == rnk(y) ? y : x);
rotate (x);
}
}
void Access (int x,int now){
for (Int y = 0;x;x = fa[y = x]){
Splay (x);
if (id[x]) T1.modify (id[x],len[x]);
son[x][1] = y,id[x] = now;
}
}
}T2; signed main(){
read (n,m),scanf ("%s",s + 1);
for (Int i = 1;i <= n;++ i) extend (s[i] - '0'),git[i] = lst;
for (Int i = 1,qL,qR;i <= m;++ i) read (qL,qR),S[qR].push_back (node{i,qL,qR});
for (Int i = 1;i <= cnt;++ i) T2.fa[i] = fail[i];
for (Int i = 1;i <= n;++ i){
T2.Access (git[i],i);
for (node g : S[i]) ans[g.ind] = T1.query (g.qL);
}
for (Int i = 1;i <= m;++ i) write (ans[i]),putchar ('\n');
return 0;
}

题解「雅礼集训 2017 Day7」事情的相似度的更多相关文章

  1. 「雅礼集训 2017 Day7」事情的相似度

    「雅礼集训 2017 Day7」事情的相似度 题目链接 我们先将字符串建后缀自动机.然后对于两个前缀\([1,i]\),\([1,j]\),他们的最长公共后缀长度就是他们在\(fail\)树上对应节点 ...

  2. 【刷题】LOJ 6041 「雅礼集训 2017 Day7」事情的相似度

    题目描述 人的一生不仅要靠自我奋斗,还要考虑到历史的行程. 历史的行程可以抽象成一个 01 串,作为一个年纪比较大的人,你希望从历史的行程中获得一些姿势. 你发现在历史的不同时刻,不断的有相同的事情发 ...

  3. 【LOJ 6041】「雅礼集训 2017 Day7」事情的相似度

    Description 人的一生不仅要靠自我奋斗,还要考虑到历史的行程. 历史的行程可以抽象成一个 01 串,作为一个年纪比较大的人,你希望从历史的行程中获得一些姿势. 你发现在历史的不同时刻,不断的 ...

  4. 【loj6041】「雅礼集训 2017 Day7」事情的相似度 后缀自动机+STL-set+启发式合并+离线+扫描线+树状数组

    题目描述 给你一个长度为 $n$ 的01串,$m$ 次询问,每次询问给出 $l$ .$r$ ,求从 $[l,r]$ 中选出两个不同的前缀的最长公共后缀长度的最大值. $n,m\le 10^5$ 题解 ...

  5. loj#6041. 「雅礼集训 2017 Day7」事情的相似度(后缀自动机+启发式合并)

    题面 传送门 题解 为什么成天有人想搞些大新闻 这里写的是\(yyb\)巨巨说的启发式合并的做法(虽然\(LCT\)的做法不知道比它快到哪里去了--) 建出\(SAM\),那么两个前缀的最长公共后缀就 ...

  6. LOJ #6041. 「雅礼集训 2017 Day7」事情的相似度

    我可以大喊一声这就是个套路题吗? 首先看到LCP问题,那么套路的想到SAM(SA的做法也有) LCP的长度是它们在parent树上的LCA(众所周知),所以我们考虑同时统计多个点之间的LCA对 树上问 ...

  7. loj#6041. 「雅礼集训 2017 Day7」事情的相似度(SAM set启发式合并 二维数点)

    题意 题目链接 Sol 只会后缀数组+暴躁莫队套set\(n \sqrt{n} \log n\)但绝对跑不过去. 正解是SAM + set启发式合并 + 二维数点/ SAM + LCT 但是我只会第一 ...

  8. 【LOJ6041】「雅礼集训 2017 Day7」事情的相似度(用LCT维护SAM的parent树)

    点此看题面 大致题意: 给你一个\(01\)串,每次询问前缀编号在一段区间内的两个前缀的最长公共后缀的长度. 离线存储询问 考虑将询问离线,按右端点大小用邻接表存下来(直接排序当然也可以啦). 这样的 ...

  9. LOJ #6041. 「雅礼集训 2017 Day7」事情的相似度 LCT+SAM+线段树

    Code: #include<bits/stdc++.h> #define maxn 200003 using namespace std; void setIO(string s) { ...

随机推荐

  1. 前端性能优化(四)——网页加载更快的N种方式

    网站前端的用户体验,决定了用户是否想要继续使用网站以及网站的其他功能,网站的用户体验佳,可留住更多的用户.除此之外,前端优化得好,还可以为企业节约成本.那么我们应该如何对我们前端的页面进行性能优化呢? ...

  2. eslint and stylelint config

    eslint: module.exports = {   root: true,   env: {     browser: true,     es6: true,     node: true   ...

  3. Django——session保持登录

    Django操作session语法: # 1.设置Sessions值 request.session['session_name'] ="admin" # 2.获取Sessions ...

  4. adb 常用命令大全(5)- 日志相关

    前言 Android 系统的日志分为两部分 底层的 Linux 内核日志输出到 /proc/kmsg Android 的日志输出到 /dev/log 语法格式 adb logcat [<opti ...

  5. VMware安装IPFire防火墙镜像

    之后便可以通过WEB登录到管理页面(admin账号,密码是在上面配置的) 详细可参考:https://www.mobibrw.com/2016/4900

  6. [ Skill ] Cadence Skill 语言入门

    https://www.cnblogs.com/yeungchie/ 写个大笔记,低速更新中 ... Cadence Skill Cadence 提供二次开发的 SKILL 语言,它是一种基于通用人工 ...

  7. liquibase新增字段注释导致表格注释同时变更bug记录

    liquibase是一个用于数据库变更跟踪.版本管理和自动部署的开源工具.它的使用方式方法可以参考官方文档或者其他人的博客,这里不做过多介绍. 1. 问题复现 在使用过程中发现了一个版本bug.这个b ...

  8. Markdown主要语法及使用

    最近,我发现使用Markdown这一标记语言的人越来越多了,我也去试了一下,感觉确实在编辑文档上方便了很多.于是我将一些关于Markdown的语法和编写时的快捷键整理在这里,方便以后查阅,也欢迎评论区 ...

  9. windows下配置VSCode免密SSH连接Linux机器

    先决条件 Windows下安装openssh软件(win10自带,可以不用搞) 从官网下载最新版本默认安装即可 VSCode安装插件 VSCode官方市场获取两个插件:"Remote - S ...

  10. 创建一个新的解耦的Orchard Core CMS网站

    引言本文将介绍创建一个功能齐全.解耦的CMS网站的过程,该网站允许您编辑博客帖子并呈现它们.解耦是一种开发模型,其中站点的前端和后端(管理)托管在同一个Web应用程序中,但只有后端由CMS驱动.然后, ...