Description

人的一生不仅要靠自我奋斗,还要考虑到历史的行程。

历史的行程可以抽象成一个 01 串,作为一个年纪比较大的人,你希望从历史的行程中获得一些姿势。

你发现在历史的不同时刻,不断的有相同的事情发生。比如,有两个人同时在世纪之交 11 年的时候上台,同样喜欢与洋人谈笑风生,同样提出了以「三」字开头的理论。

你发现,一件事情可以看成是这个 01 串的一个前缀,这个前缀最右边的位置就是这个事情的结束时间。

两件事情的相似度可以看成,这两个前缀的最长公共后缀长度。

现在你很好奇,在一段区间内结束的事情中最相似的两件事情的相似度是多少呢?

Solution

考虑暴力做法,离线询问

因为两个串的最长公共后缀,就是所代表节点的 \(lca\) 的 \(len\)

每一次加入一个前缀,在 \(parent\) 树上往上跳,如果一个点被跳过我们就更新答案

因为右端点固定时,左端点越大,对询问的贡献肯定越多,所以直接覆盖掉这个节点事件的下标(也就是 \(pos\)),所以我们维护这个节点子树内的最大 \(pos\) 值就行了

但是还有一个左端点限制,我们开一个左端点为下标的树状数组维护一下就好了

实际上这个过程就是 \(LCT\) 的 \(access\),那么用 \(LCT\) 做这个过程复杂度就可以均摊为 \(access\) 的复杂度了

#include<bits/stdc++.h>
using namespace std;
const int N=2e5+10;
template<class T>void gi(T &x){
int f;char c;
for(f=1,c=getchar();c<'0'||c>'9';c=getchar())if(c=='-')f=-1;
for(x=0;c>='0'&&c<='9';c=getchar())x=x*10+(c&15);x*=f;
}
int fa[N],ch[N][2],len[N],cur=1,cnt=1,n,Q,pos[N];
char s[N];int tr[N],ans[N];
struct data{int x,id;};
vector<data>v[N];vector<data>::iterator it;
inline void add(int x,int y){for(int i=x;i>=1;i-=(i&(-i)))tr[i]=max(tr[i],y);}
inline int qry(int x){
int ret=0;
for(int i=x;i<=n;i+=(i&(-i)))ret=max(ret,tr[i]);
return ret;
}
namespace lct{
int fa[N],ch[N][2],w[N],la[N];
inline void mark(int x,int y){w[x]=y;la[x]=y;}
inline bool isrt(int x){return ch[fa[x]][0]!=x&&ch[fa[x]][1]!=x;}
inline void rotate(int x){
int y=fa[x];bool t=ch[y][1]==x;
ch[y][t]=ch[x][!t];fa[ch[y][t]]=y;
ch[x][!t]=y;fa[x]=fa[y];
if(!isrt(y))ch[fa[y]][ch[fa[y]][1]==y]=x;
fa[y]=x;
}
inline void pushdown(int x){
if(!la[x])return ;
mark(ch[x][0],la[x]);mark(ch[x][1],la[x]);la[x]=0;
}
inline void Push(int x){if(!isrt(x))Push(fa[x]);pushdown(x);}
inline void splay(int x){
Push(x);
while(!isrt(x)){
int y=fa[x],p=fa[y];
if(isrt(y))rotate(x);
else if((ch[p][0]==y)==(ch[y][0]==x))rotate(y),rotate(x);
else rotate(x),rotate(x);
}
}
inline void access(int x,int id){
int y=0;
while(x)splay(x),ch[x][1]=y,add(w[x],len[x]),x=fa[y=x];
mark(y,id);
}
}
inline void ins(int c){
int p=cur;cur=++cnt;len[cur]=len[p]+1;
for(;p && !ch[p][c];p=fa[p])ch[p][c]=cur;
if(!p)fa[cur]=1;
else{
int q=ch[p][c];
if(len[p]+1==len[q])fa[cur]=q;
else{
int nt=++cnt;len[nt]=len[p]+1;
memcpy(ch[nt],ch[q],sizeof(ch[q]));
fa[nt]=fa[q];fa[cur]=fa[q]=nt;
for(;p && ch[p][c]==q;p=fa[p])ch[p][c]=nt;
}
}
}
int main(){
freopen("pp.in","r",stdin);
freopen("pp.out","w",stdout);
int x,y;
cin>>n>>Q;
scanf("%s",s+1);
for(int i=1;i<=n;i++)ins(s[i]-'0'),pos[i]=cur;
for(int i=1;i<=Q;i++)gi(x),gi(y),v[y].push_back((data){x,i});
for(int i=2;i<=cnt;i++)lct::fa[i]=fa[i];
for(int i=1;i<=n;i++){
lct::access(pos[i],i);
for(it=v[i].begin();it!=v[i].end();++it)ans[it->id]=qry(it->x);
}
for(int i=1;i<=Q;i++)printf("%d\n",ans[i]);
return 0;
}

LOJ #6041. 事情的相似度的更多相关文章

  1. 【LOJ#6041】事情的相似度(后缀自动机)

    [LOJ#6041]事情的相似度(后缀自动机) 题面 LOJ 题解 \(\mbox{YCB}\)搬了这道题目...\(\mbox{QwQ}\) 还是用到\(lcp\)就是\(parent\)树上的\( ...

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

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

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

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

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

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

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

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

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

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

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

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

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

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

  9. #6041. 「雅礼集训 2017 Day7」事情的相似度 [set启发式合并+树状数组扫描线]

    SAM 两个前缀的最长后缀等价于两个点的 \(len_{lca}\) , 题目转化为求 \(l \leq x , y \leq r\) , \(max\{len_{lca(x,y)}\}\) // p ...

随机推荐

  1. Java作业-网络编程

    Java网络编程 关于结合以前的大作业(即我的图书馆管理系统) 我感觉,图书馆管理系统更像是一个偏向于B/S模式的体系,如果想让他可用性变得更好,可以优化的地方只有使用数据库来代替文件,我个人是没有想 ...

  2. 201621123060 《Java程序设计》第六周学习总结

    1. 本周学习总结 1.1 面向对象学习暂告一段落,请使用思维导图,以封装.继承.多态为核心概念画一张思维导图或相关笔记,对面向对象思想进行一个总结. 注1:关键词与内容不求多,但概念之间的联系要清晰 ...

  3. 如何减小ios安装包大小

    以前的老文章了,搬到cnblog 更小的安装包意味着更快的下载安装速度,也往往意味着更快的加载运行速度,是优化ios应用的一个重要方面,本文主要参考<减小iOS应用程序的大小>,在实际测试 ...

  4. C++布隆过滤器

    布隆过滤器 这名词有没有听着好像很 挺高大上的,的确,它也是一种很重要的结构,下面一起看看: 一:说说历史: (Bloom Filter)是由布隆(Burton Howard Bloom)在1970年 ...

  5. 静态关键字static用法。

    static的特点:1,static是一个修饰符,用于修饰成员.2,static修饰的成员被所有的对象所共享.3,static优先于对象存在,因为static的成员随着类的加载就已经存在了. 4,st ...

  6. NOIP2016 天天爱跑步 80分暴力

    https://www.luogu.org/problem/show?pid=1600 题目描述 小c同学认为跑步非常有趣,于是决定制作一款叫做<天天爱跑步>的游戏.«天天爱跑步»是一个养 ...

  7. $.each遍历json数组

    1.遍历单层json数组 我们把idx和obj都打印出来看看,到底是什么东西 var json1 =[{"id":"1","tagName" ...

  8. java排序算法之冒泡排序(Bubble Sort)

    java排序算法之冒泡排序(Bubble Sort) 原理:比较两个相邻的元素,将值大的元素交换至右端. 思路:依次比较相邻的两个数,将小数放在前面,大数放在后面.即在第一趟:首先比较第1个和第2个数 ...

  9. Windows中添加自己的程序到开机启动中(添加服务,添加注册表)

    在系统启动的时候启动自己想要启动的程序: 方法一:利用开机启动文件夹 将exe文件或exe文件的快捷方式复制到(启动)文件夹下 以win7为例:开始→所有程序→启动→鼠标右键打开 方法二:添加系统服务 ...

  10. netty : NioEventLoopGroup 源码分析

    NioEventLoopGroup 源码分析 1. 在阅读源码时做了一定的注释,并且做了一些测试分析源码内的执行流程,由于博客篇幅有限.为了方便 IDE 查看.跟踪.调试 代码,所以在 github ...