【loj6041】「雅礼集训 2017 Day7」事情的相似度 后缀自动机+STL-set+启发式合并+离线+扫描线+树状数组
给你一个长度为 $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+启发式合并+离线+扫描线+树状数组的更多相关文章
- LOJ6041. 「雅礼集训 2017 Day7」事情的相似度 [后缀树,LCT]
LOJ 思路 建出反串的后缀树,发现询问就是问一个区间的点的\(lca\)的深度最大值. 一种做法是dfs的时候从下往上合并\(endpos\)集合,发现插入一个点的时候只需要把与前驱后继的贡献算进去 ...
- 「雅礼集训 2017 Day7」事情的相似度
「雅礼集训 2017 Day7」事情的相似度 题目链接 我们先将字符串建后缀自动机.然后对于两个前缀\([1,i]\),\([1,j]\),他们的最长公共后缀长度就是他们在\(fail\)树上对应节点 ...
- 【LOJ 6041】「雅礼集训 2017 Day7」事情的相似度
Description 人的一生不仅要靠自我奋斗,还要考虑到历史的行程. 历史的行程可以抽象成一个 01 串,作为一个年纪比较大的人,你希望从历史的行程中获得一些姿势. 你发现在历史的不同时刻,不断的 ...
- 【刷题】LOJ 6041 「雅礼集训 2017 Day7」事情的相似度
题目描述 人的一生不仅要靠自我奋斗,还要考虑到历史的行程. 历史的行程可以抽象成一个 01 串,作为一个年纪比较大的人,你希望从历史的行程中获得一些姿势. 你发现在历史的不同时刻,不断的有相同的事情发 ...
- 【LOJ6041】「雅礼集训 2017 Day7」事情的相似度(用LCT维护SAM的parent树)
点此看题面 大致题意: 给你一个\(01\)串,每次询问前缀编号在一段区间内的两个前缀的最长公共后缀的长度. 离线存储询问 考虑将询问离线,按右端点大小用邻接表存下来(直接排序当然也可以啦). 这样的 ...
- LOJ #6041. 「雅礼集训 2017 Day7」事情的相似度
我可以大喊一声这就是个套路题吗? 首先看到LCP问题,那么套路的想到SAM(SA的做法也有) LCP的长度是它们在parent树上的LCA(众所周知),所以我们考虑同时统计多个点之间的LCA对 树上问 ...
- loj#6041. 「雅礼集训 2017 Day7」事情的相似度(SAM set启发式合并 二维数点)
题意 题目链接 Sol 只会后缀数组+暴躁莫队套set\(n \sqrt{n} \log n\)但绝对跑不过去. 正解是SAM + set启发式合并 + 二维数点/ SAM + LCT 但是我只会第一 ...
- loj#6041. 「雅礼集训 2017 Day7」事情的相似度(后缀自动机+启发式合并)
题面 传送门 题解 为什么成天有人想搞些大新闻 这里写的是\(yyb\)巨巨说的启发式合并的做法(虽然\(LCT\)的做法不知道比它快到哪里去了--) 建出\(SAM\),那么两个前缀的最长公共后缀就 ...
- LOJ #6041. 「雅礼集训 2017 Day7」事情的相似度 LCT+SAM+线段树
Code: #include<bits/stdc++.h> #define maxn 200003 using namespace std; void setIO(string s) { ...
随机推荐
- 20155318 2016-2017-2 《Java程序设计》第一周学习总结
20155318 2016-2017-2 <Java程序设计>第一周学习总结 教材学习内容总结 上周总结 上周学习了一些大学的学习方法,比如知识分为为三种:元知识.软知识和硬知识,讲述技能 ...
- angular中的$q服务实例
用于理解$q服务 参考:http://www.zouyesheng.com/angular.html#toc39 广义回调管理 和其它框架一样, ng 提供了广义的异步回调管理的机制. $http 服 ...
- Drupal7重置密码方法
Drupal版本 7.40 方法1: 根目录index.php添加 require_once 'includes/password.inc'; require_once 'includes/boots ...
- springmvc 使用 response 的注意事项以及解决500 空指针异常找不到 response 的方法
使用注解方式在类中(Controller)来装载request时,是可以正常使用request的(必须在启动时才注入,所以不支持热部署),但是同样使用这种方式在已经装载了 request的情况下装载 ...
- 一、EnterpriseFrameWork框架总体介绍
EnterpriseFrameWork框架是自己在工作之余的得意之作,经过了几年时间的不断重构,现在终于有了现在的样子:刚开始只是为了方便开发WEB系统,随着项目越做越多,新的功能也就不断补充进去,补 ...
- 在腾讯云上安装mysql遇到的问题
卸载mysql: 1.sudo apt-get autoremove --purge mysql-server-5.5 5.5 是数据库版本, mysql -v 显示版本信息 2.sudo apt-g ...
- AndroidStudio 新建不同的Drawable文件夹
以前习惯eclipse开发Android的朋友们知道 新创建一个Android项目的时候eclipse会自动生成多个drawable文件夹来存放图片 但是Android Studio 新建项目的时候只 ...
- 测试Websocket建立通信,使用protobuf格式交换数据
接到一个应用测试,应用实现主要使用websocket保持长链接,使用protobuf格式交换数据,用途为发送消息,需要我们测试评估性能,初步评估需要测试长链接数.峰值消息数以及长期运行稳定性 整体需求 ...
- Java EE JSP内置对象及表达式语言
一.JSP内置对象 JSP根据Servlet API规范提供了一些内置对象,开发者不用事先声明就可使用标准变量来访问这些对象. JSP提供了9种内置对象: (一).request 简述: JSP编程中 ...
- Linux内核学习笔记(2)-- 父进程和子进程及它们的访问方法
Linux系统中,进程之间有一个明显的继承关系,所有进程都是 PID 为1的 init 进程的后代.内核在系统启动的最后阶段启动 init 进程.该进程读取系统的初始化脚本(initscript)并执 ...