LOJ #6041. 「雅礼集训 2017 Day7」事情的相似度 LCT+SAM+线段树
我们有一个经典模型:
两个串的最长公共后缀长度,是后缀树中两点 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+线段树的更多相关文章
- 【刷题】LOJ 6041 「雅礼集训 2017 Day7」事情的相似度
题目描述 人的一生不仅要靠自我奋斗,还要考虑到历史的行程. 历史的行程可以抽象成一个 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\),那么两个前缀的最长公共后缀就 ...
- #6041. 「雅礼集训 2017 Day7」事情的相似度 [set启发式合并+树状数组扫描线]
SAM 两个前缀的最长后缀等价于两个点的 \(len_{lca}\) , 题目转化为求 \(l \leq x , y \leq r\) , \(max\{len_{lca(x,y)}\}\) // p ...
- 「雅礼集训 2017 Day7」事情的相似度
「雅礼集训 2017 Day7」事情的相似度 题目链接 我们先将字符串建后缀自动机.然后对于两个前缀\([1,i]\),\([1,j]\),他们的最长公共后缀长度就是他们在\(fail\)树上对应节点 ...
- 【LOJ 6041】「雅礼集训 2017 Day7」事情的相似度
Description 人的一生不仅要靠自我奋斗,还要考虑到历史的行程. 历史的行程可以抽象成一个 01 串,作为一个年纪比较大的人,你希望从历史的行程中获得一些姿势. 你发现在历史的不同时刻,不断的 ...
- LOJ6041. 「雅礼集训 2017 Day7」事情的相似度 [后缀树,LCT]
LOJ 思路 建出反串的后缀树,发现询问就是问一个区间的点的\(lca\)的深度最大值. 一种做法是dfs的时候从下往上合并\(endpos\)集合,发现插入一个点的时候只需要把与前驱后继的贡献算进去 ...
- 【loj6041】「雅礼集训 2017 Day7」事情的相似度 后缀自动机+STL-set+启发式合并+离线+扫描线+树状数组
题目描述 给你一个长度为 $n$ 的01串,$m$ 次询问,每次询问给出 $l$ .$r$ ,求从 $[l,r]$ 中选出两个不同的前缀的最长公共后缀长度的最大值. $n,m\le 10^5$ 题解 ...
随机推荐
- MSXML2
The following C/C++ example creates and appends a new text node to the root document element. #impor ...
- nyoj_655_光棍的yy_201311281539
光棍的yy 时间限制:1000 ms | 内存限制:65535 KB 难度:2 描述 yy经常遇见一个奇怪的事情,每当他看时间的时候总会看见11:11,这个很纠结啊. 现在给 ...
- N - Corporate Identity
Beside other services, ACM helps companies to clearly state their “corporate identity”, which includ ...
- iOS: 在Swift中优雅的实现Substring
在Swift中,当我们想要截取某个字符串时,方法如下: let carNumber = "沪A12345" let startIndex = advance(userCar.car ...
- Android中@+id和@id的差别
Android中的组件须要用一个int类型的值来表示.这个值也就是组件标签中的id属性值. id属性仅仅能接受资源类型的值,也就是必须以@开头的值,比如,@id/abc.@+id/xyz等. 假设在 ...
- [Windows]_[0基础]_[Release程序的崩溃报告minidump解决方式]
场景: 1. Release的程序崩溃时,崩溃报告能够让开发者查明代码哪里出了问题,用处大大的. 2. 仅仅实用VS的编译器才支持,所以MinGW就无缘了. 3. 使用了未处理异常过滤处理函数. 4. ...
- 安装Office Web Apps Server 2013 – KB2592525安装失败
在Windows Server 2008 R2上安装 office web apps Server 的其中一个先决条件是 安装KB2592525. 但由于服务器已经打了SP1及其它大部分的patch, ...
- 深入剖析Android四大组件(一)——Activity生命周期具体解释
1.管理Activity的生命周期 不管是正在执行的Activity还是没有执行的Activity,它们都接受Android的框架管理,这使得Activity处于不同的生命周期. ①Activity的 ...
- 【Ubuntu】基本操作 (条目=11)
定义 NAME 为要操作的对象名 定义 DIR 为文件所在的绝对路径 所有操作默认在普通用户下进行 所有软件包默认是指Debian包(deb包) 1.查看进程 top 2.强制结束进程 PID由top ...
- C# SuperWebSocket服务端学习(二)
首先需要下载DLL类库 地址详见:http://download.csdn.NET/detail/u011269801/9590935 1,打开VS2012,新建一个控制台应用程序,选择.NET4. ...