题目描述

给你一个长度为 $n$ 的01串,$m$ 次询问,每次询问给出 $l$ 、$r$ ,求从 $[l,r]$ 中选出两个不同的前缀的最长公共后缀长度的最大值。

$n,m\le 10^5$


题解

后缀自动机+STL-set+启发式合并+离线+扫描线+树状数组

两个前缀的最长公共后缀,在正串后缀自动机上体现为pre树上两点LCA的深度。

考虑统计pre树上一个点的贡献:对于两个前缀 $x$ 、$y$ ,它能够影响的询问左端点小于等于 $x$ ,右端点大于等于 $y$ 。因此影响最大化的前缀对就是排序后相邻的两个,每次只需要考虑这些前缀对。

那么我们考虑两个子树合并的过程,使用STL-set维护前驱后继成为贡献。这个过程可以启发式合并,把小的合并到大的中。

剩下的就是对于询问 $[l,r]$ ,询问前缀对中第一个 $\ge l$ ,第二个 $\le r$ 的最大值。离线+扫描线+树状数组维护前缀最小值即可。

时间复杂度瓶颈在于启发式合并STL-set,为 $O(n\log^2n)$ 。

#include <set>
#include <cstdio>
#include <cstring>
#include <algorithm>
#define N 200010
using namespace std;
struct data
{
int x , y , z;
data() {}
data(int a , int b , int c) {x = a , y = b , z = c;}
bool operator<(const data &a)const {return x < a.x;}
}a[N * 20] , q[N];
set<int> s[N];
int pre[N] , c[N][2] , dis[N] , val[N] , last = 1 , tot = 1 , head[N] , to[N] , next[N] , cnt , bl[N] , ta , f[N] , n , ans[N];
char str[N];
void insert(int k , int ch)
{
int p = last , np = last = ++tot;
dis[np] = dis[p] + 1 , s[np].insert(k);
while(p && !c[p][ch]) c[p][ch] = np , p = pre[p];
if(!p) pre[np] = 1;
else
{
int q = c[p][ch];
if(dis[q] == dis[p] + 1) pre[np] = q;
else
{
int nq = ++tot;
memcpy(c[nq] , c[q] , sizeof(c[q]));
dis[nq] = dis[p] + 1 , pre[nq] = pre[q] , pre[np] = pre[q] = nq;
while(p && c[p][ch] == q) c[p][ch] = nq , p = pre[p];
}
}
}
inline void add(int x , int y)
{
to[++cnt] = y , next[cnt] = head[x] , head[x] = cnt;
}
void dfs(int x)
{
int i;
set<int>::iterator j , k;
for(i = head[x] ; i ; i = next[i])
{
dfs(to[i]);
if(s[bl[x]].size() > s[bl[to[i]]].size())
{
for(j = s[bl[to[i]]].begin() ; j != s[bl[to[i]]].end() ; j ++ )
{
k = s[bl[x]].upper_bound(*j);
if(k != s[bl[x]].end()) a[++ta] = data(*j , *k , dis[x]);
if(k != s[bl[x]].begin()) a[++ta] = data(*--k , *j , dis[x]);
s[bl[x]].insert(*j);
}
}
else
{
for(j = s[bl[x]].begin() ; j != s[bl[x]].end() ; j ++ )
{
k = s[bl[to[i]]].upper_bound(*j);
if(k != s[bl[to[i]]].end()) a[++ta] = data(*j , *k , dis[x]);
if(k != s[bl[to[i]]].begin()) a[++ta] = data(*--k , *j , dis[x]);
s[bl[to[i]]].insert(*j);
}
bl[x] = bl[to[i]];
}
}
}
inline void fix(int x , int a)
{
int i;
for(i = x ; i <= n ; i += i & -i) f[i] = max(f[i] , a);
}
inline int query(int x)
{
int i , ans = 0;
for(i = x ; i ; i -= i & -i) ans = max(ans , f[i]);
return ans;
}
int main()
{
int m , i , p;
scanf("%d%d%s" , &n , &m , str + 1);
for(i = 1 ; i <= n ; i ++ ) insert(i , str[i] - '0');
for(i = 2 ; i <= tot ; i ++ ) add(pre[i] , i);
for(i = 1 ; i <= tot ; i ++ ) bl[i] = i;
dfs(1);
for(i = 1 ; i <= m ; i ++ ) scanf("%d%d" , &q[i].x , &q[i].y) , q[i].z = i;
sort(a + 1 , a + ta + 1) , sort(q + 1 , q + m + 1);
for(p = ta , i = m ; i ; i -- )
{
while(p && a[p].x >= q[i].x) fix(a[p].y , a[p].z) , p -- ;
ans[q[i].z] = query(q[i].y);
}
for(i = 1 ; i <= m ; i ++ ) printf("%d\n" , ans[i]);
return 0;
}

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

  1. LOJ6041. 「雅礼集训 2017 Day7」事情的相似度 [后缀树,LCT]

    LOJ 思路 建出反串的后缀树,发现询问就是问一个区间的点的\(lca\)的深度最大值. 一种做法是dfs的时候从下往上合并\(endpos\)集合,发现插入一个点的时候只需要把与前驱后继的贡献算进去 ...

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

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

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

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

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

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

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

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

  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. loj#6041. 「雅礼集训 2017 Day7」事情的相似度(后缀自动机+启发式合并)

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

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

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

随机推荐

  1. 20155327李百乾 Exp3 免杀原理与实践

    20155327李百乾 Exp3 免杀原理与实践 实践guocheng 一.Msfvenom使用编码器 1.利用(virustota)[https://www.virustotal.com/]检测实验 ...

  2. hasOwnProperty()函数

    hasOwnProperty()函数的返回值为Boolean类型.如果对象object具有名称为propertyName的属性,则返回true,否则返回false 例子: function Site( ...

  3. !important用法:

    如果不加important特性,则超链接的颜色为蓝色,但是加上important之后优先级提高了,显示颜色为绿色 <style type="text/css"> a{ ...

  4. GridSQL--Stado 学习初步

    磨砺技术珠矶,践行数据之道,追求卓越价值 回到上一级页面: PostgreSQL集群方案相关索引页     回到顶级页面:PostgreSQL索引页 作者  高健@博客园   luckyjackgao ...

  5. Dlib简介及在windows7 vs2013编译过程

    Dlib是一个C++库,包含了许多机器学习算法.它是跨平台的,可以应用在Windows.Linux.Mac.embedded devices.mobile phones等.它的License是Boos ...

  6. 【LG4841】城市规划

    [LG4841]城市规划 题面 洛谷 题解 记\(t_i\)表示\(i\)个点的无向图个数,显然\(t_i=2^{C_i^2}\). 设\(f_i\)表示\(i\)个点的无向连通图个数,容斥一下,枚举 ...

  7. Drupal7重置密码方法

    Drupal版本 7.40 方法1: 根目录index.php添加 require_once 'includes/password.inc'; require_once 'includes/boots ...

  8. web存储机制(localStorage和sessionStorage)

    web存储包括两种:sessionStorage 和 localStorage(都是限定在文档源级别,非同源文档间无法共享) 1.sessionStorage 数据放在服务器上(IE不支持) 严格用于 ...

  9. python常用模块详解2

    序列化模块补充: 1.json格式的限制,json格式的key必须是字符串数据类型 2.json格式的字符串必须是"" 如果数字是key,那么dump之后会强转成字符串数据类型 i ...

  10. 集群服务器、负载均衡和session共享,C#的static变量

    集群服务器:是指由两台以上服务器共同组成的服务器,目的是为了提高性能. 负载均衡:是基于集群服务器实现的,作用是当A服务器访问数达到一定上限时,接下来客户端的请求会自动分配给B服务器,目的是减少服务器 ...