题目传送门

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. 前端下载文档的java工具类

    package com.ry.project.util.commUtil;import freemarker.template.Configuration;import freemarker.temp ...

  2. 真实机中安装CentOS

    前言 最近在b站上看了兄弟连老师的Linux教程,非常适合入门:https://www.bilibili.com/video/BV1mW411i7Qf 看完后就自己来试着玩下,正好手上有台空闲的电脑就 ...

  3. Hutool中那些常用的工具类和方法

    Hutool中那些常用的工具类和方法 Hutool是一个Java工具包,它帮助我们简化每一行代码,避免重复造轮子.如果你有需要用到某些工具方法的时候,不妨在Hutool里面找找,可能就有.本文将对Hu ...

  4. a、b、n为正整数且a>b,证明:若n|(a^n-b^n),则n|(a^n-b^n)/(a-b).

  5. ES6中函数调用自身需要注意的问题

    在传统的递归调用中,可以采用如下方式 function sum(n) { return sum(n - 1) + n;} 但如今es6盛行,为了保持代码一致性,可以采用两种解决方式. 第一种,将thi ...

  6. golang channel原理

    channel介绍 channel一个类型管道,通过它可以在goroutine之间发送和接收消息.它是Golang在语言层面提供的goroutine间的通信方式. 众所周知,Go依赖于称为CSP(Co ...

  7. Excel 快速跳转到工作表

    新建 vba 模块 Sub GotoSheet() tname = InputBox("input table name") If StrPtr(tname) = 0 Then E ...

  8. python库--pandas--部分实例

    >>> pd.pivot( index=np.array(['one', 'one', 'one', 'two', 'two', 'two']), columns=np.array( ...

  9. Spring Cloud Eureka 之常用配置解析

    [原创内容,转载.引用请注明出处] 1. 配置项解析 1.1 通用配置 # 应用名称,将会显示在Eureka界面的应用名称列 spring.application.name=config-servic ...

  10. CodeForce-785B Anton and Classes(简单贪心)

    Anton and Classes Anton likes to play chess. Also he likes to do programming. No wonder that he deci ...