我们有一个经典模型:

两个串的最长公共后缀长度,是后缀树中两点 LCA 的深度.

直接求 LCA 似乎有些困难,不妨这样想 :

设两个串在后缀树中对应的点分别为 $a,b$,将 $a$ 到根的路径涂色,$b$ 向根爬,遇到的第一个涂色点即为 $a$ 与 $b$ 的 LCA.

我们用 $LCT$ 来维护 这颗树,涂色操作直接 $Access$ 并区间赋值.

此时树的形态是怎样的 ?

$a$ 点到根的路径是一个重链,$b$ 向上爬肯定会爬到 $a$ 所在的重链上.

直接爬肯定会很慢,不妨直接 $Access(b)$ ?

考虑 $Access$ 时的语句 : $y=x,x=f_{x}$.

我们在 $Access(b)$ 过程中碰到的第一个被涂色的点其实就是我们要求的 LCA.

尤其当被涂色的点变多的时候,每次 $Access$ 的复杂度就是均摊 $log_{2}{n}$ 的了.

是不是十分神奇 ?

观察这部分代码:

void Access(int x,int co)
{
int t=0;
while(x)
{
splay(x);
if(pos[x]) tr::update(1,n,1,pos[x],dis[x]);
pos[x]=co, rson=t, t=x,x=f[x];
}
}

我们一边 $Access$ ,一边将该询问点向上爬,每次遇到一个新的重链就将重链染成当前颜色(区间中的下标).可能会好奇为什么只更新那个下标更小的呢 ?

因为如果更新下标大的,而不更新下标小的,会出现这种情况:

当前询问区间左端点大于下标小的那个,那么显然下标大的继承不了下标小的的贡献.

为了避免这种情况,也就是说希望贡献给对未来没有影响的那个,我们只更新给下标更小的那个. (如果询问左端点小于下标小的点的话一定是能覆盖到的)

而下标大的点如何处理呢 ?

既然不对答案做贡献,我们将遇到的每一个重链都染成更大的下标. (显然我们以后要染的下标肯定都会大于该下标,所以肯定下标越大越好).

最难的部分处理完毕了,查询的时候直接在对应的线段树里查一个区间最大值即可.  

#include<bits/stdc++.h>
#define maxn 200003
using namespace std;
void setIO(string s)
{
string in=s+".in", out=s+".out";
freopen(in.c_str(),"r",stdin);
// freopen(out.c_str(),"w",stdout);
}
int n,Q;
int dis[maxn],track[maxn];
char str[maxn];
namespace tr
{
int maxv[maxn<<2];
void update(int l,int r,int x,int k,int p)
{
if(l==r)
{
maxv[x]=p;
return;
}
int mid=(l+r)>>1;
if(k<=mid) update(l,mid,(x<<1),k,p);
else update(mid+1,r,(x<<1)|1,k,p);
maxv[x]=max(maxv[x<<1],maxv[(x<<1)|1]);
}
int query(int l,int r,int x,int L,int R)
{
if(l>=L&&r<=R) return maxv[x];
int mid=(l+r)>>1,tmp=0;
if(L<=mid) tmp=max(tmp,query(l,mid,x<<1,L,R));
if(R>mid) tmp=max(tmp,query(mid+1,r,(x<<1)|1,L,R));
return tmp;
}
};
namespace tree
{
#define lson ch[x][0]
#define rson ch[x][1]
int f[maxn],ch[maxn][30],pos[maxn],sta[maxn];
int get(int x)
{
return ch[f[x]][1]==x;
}
int isrt(int x)
{
return !(ch[f[x]][0]==x||ch[f[x]][1]==x);
}
void pushdown(int x)
{
if(!x)return;
if(pos[x])
{
if(lson) pos[lson]=pos[x];
if(rson) pos[rson]=pos[x];
}
}
void rotate(int x)
{
int old=f[x], fold=f[old],which=get(x);
if(!isrt(old)) ch[fold][ch[fold][1]==old]=x;
ch[old][which]=ch[x][which^1],f[ch[old][which]]=old;
ch[x][which^1]=old,f[old]=x,f[x]=fold;
}
void splay(int x)
{
int u=x,v=0,fa;
sta[++v]=u;
while(!isrt(u)) sta[++v]=f[u],u=f[u];
while(v) pushdown(sta[v--]);
for(u=f[u];(fa=f[x])!=u;rotate(x))
if(f[fa]!=u)
rotate(get(fa)==get(x)?fa:x);
}
void Access(int x,int co)
{
int t=0;
while(x)
{
splay(x);
if(pos[x]) tr::update(1,n,1,pos[x],dis[x]);
pos[x]=co, rson=t, t=x,x=f[x];
}
}
};
namespace SAM
{
int last,tot;
int ch[maxn][30],f[maxn];
void init()
{
last=tot=1;
}
void ins(int c,int o)
{
int np=++tot,p=last;
last=np,dis[np]=dis[p]+1;
while(p&&!ch[p][c]) ch[p][c]=np,p=f[p];
if(!p) f[np]=1;
else
{
int q=ch[p][c];
if(dis[q]==dis[p]+1) f[np]=q;
else
{
int nq=++tot;
dis[nq]=dis[p]+1;
memcpy(ch[nq],ch[q],sizeof(ch[q]));
f[nq]=f[q], f[np]=f[q]=nq;
while(p&&ch[p][c]==q) ch[p][c]=nq,p=f[p]; // 割裂操作
}
}
track[o]=np;
}
};
int answer[maxn];
struct OPT
{
int l,r,id;
}opt[maxn];
bool cmp(OPT a,OPT b)
{
return a.r<b.r;
}
int main()
{
// setIO("input");
SAM::init();
scanf("%d%d",&n,&Q);
scanf("%s",str+1);
for(int i=1;i<=n;++i) SAM::ins(str[i]-'0',i);
for(int i=2;i<=SAM::tot;++i) tree::f[i]=SAM::f[i];
for(int i=1;i<=Q;++i) scanf("%d%d",&opt[i].l,&opt[i].r),opt[i].id=i;
sort(opt+1,opt+1+Q,cmp);
for(int i=1,j=1;i<=n;++i)
{
tree::Access(track[i], i); // 打上 i 点的标记
while(opt[j].r==i && j<=Q)
{
answer[opt[j].id]=tr::query(1,n,1,opt[j].l,opt[j].r);
++j;
}
}
for(int i=1;i<=Q;++i) printf("%d\n",answer[i]);
return 0;
}

  

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

随机推荐

  1. 一个最简单的SPRINGMVC示例

    持久层,服务层,表现层都涉及到了. 这个分得确实比DJANGO细致, 多体会,多注解..:) The domain layer package com.packt.webstore.domain; i ...

  2. backup script

    #!/bin/bash ##################################################### # export the whole database use ex ...

  3. QT中使用高速排序

    今天想到了用QT做一个高速排序.所以研究了一下. 由于用习惯了,C++的std::sort.就算是C的时候也用得是stdlib.h中的qsort. 手写板 手写板的快排事实上不难,仅仅是自从用C++打 ...

  4. Bitcask存储模型

    ----<大规模分布式存储系统:原理解析与架构实战>读书笔记 近期一直在分析OceanBase的源代码,恰巧碰到了OceanBase的核心开发人员的新作<大规模分布式存储系统:原理解 ...

  5. jQuery EasyUI 1.4更新记录

    问题: menu:修复当删除一个menu项时.menu高度不准确. datagrid:修复当datagrid宽度太小时,fitColumns方法不能使用. 改进: 1.为easyui全部组件新增了自适 ...

  6. 极光推送案例-PushExample-Jpush

    ssh - maven - java项目-极光注冊id完毕推送 这是我学习时的步骤: 1:去极光推送平台注冊账号,自己能够去注冊(一般公司会帮助完毕注冊) 地址:https://www.jpush.c ...

  7. C\C++控制台颜色设置类

    windows和Linux都可用的一个类...用来设置颜色,没有太复杂.简单够用吧. #ifdef _WIN32 #include <Windows.h> class FontColor ...

  8. 浅谈PHP数据结构之单链表

    什么是链表?(依据百度词条查询而得) 链表是一种物理存储单元上非连续.非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的.链表由一系列结点(链表中每个元素称为结点)组成,结点能够在执 ...

  9. Recovery 中的UI知识积累【转】

    本文转载自:http://blog.csdn.net/wed110/article/details/26554197 int gr_init(void);             /* 初始化图形显示 ...

  10. BZOJ 3727 DP?推式子..

    思路: 设$sum[i]表示i的子树中a[i]的和$ $b[1]=\Sigma a[i]*dis[i] = \Sigma _{i=2} ^n sum[i]$ $b[x]-b[fa[x]]=sum[1] ...