【刷题】LOJ 6041 「雅礼集训 2017 Day7」事情的相似度
题目描述
人的一生不仅要靠自我奋斗,还要考虑到历史的行程。
历史的行程可以抽象成一个 01 串,作为一个年纪比较大的人,你希望从历史的行程中获得一些姿势。
你发现在历史的不同时刻,不断的有相同的事情发生。比如,有两个人同时在世纪之交 11 年的时候上台,同样喜欢与洋人谈笑风生,同样提出了以「三」字开头的理论。
你发现,一件事情可以看成是这个 01 串的一个前缀,这个前缀最右边的位置就是这个事情的结束时间。
两件事情的相似度可以看成,这两个前缀的最长公共后缀长度。
现在你很好奇,在一段区间内结束的事情中最相似的两件事情的相似度是多少呢?
输入格式
第一行两个整数 \(n\)、\(m\) ,表示串长和询问个数。
第二行长度为 \(n\) 的 01 串,表示历史的行程。
接下来 \(m\) 行,每行两个正整数 \(l\) 、\(r\) 表示询问的区间,包括端点,保证 \(1 \leq l < r \leq n\) 。
输出格式
输出 \(m\) 行,对每个询问输出一个整数表示最大的相似度。
样例
样例输入 1
4 2
0101
1 3
2 4
样例输出 1
1
2
数据范围与提示
测试点 | $n$ $=$ | $m$ $=$ |
---|---|---|
1 ~ 2 | 100 | 100 |
3 ~ 4 | 300 | 1000 |
5 ~ 6 | 5000 | 5000 |
7 ~ 10 | 100000 | 300 |
11 ~ 14 | 50000 | 50000 |
15 ~ 20 | 100000 | 100000 |
题解
线段树+LCT+SAM
将询问按照右端点从小到大离线,从左往右枚举右端点,计算询问右端点为当前枚举点的答案
首先知道,两个前缀的最长后缀的长度就是它们在SAM中LCA的len
那么先搞定LCA。在extend一个字母的时候,将它到根的路径上所有的点全部打上标记,标记是当前字母的位置。那么以后如果有新字母插入的时候,如果遇到了这个标记,那么标记的那个点一定是这两个字母的公共祖先,那么它的len可能会产生贡献
那么每插入一个新节点,就遍历它到根的路径,寻找标记,计算贡献。复杂度?把这个过程放进LCT的access
对于同一个点的多个标记,由于我们的枚举方式,肯定是标记越大越优,所以extend字母要打标记的时候,直接把它到根路径上的所有点全部标记为当前位置就好了。这个操作又可以交给LCT解决
考虑怎么算答案。搞一棵线段树。在遍历节点到根的路径算贡献的时候,假设到达节点 \(i\) ,标记是 \(ps_i\) ,那么在线段树 \(ps_i\) 的位置上chkmax一下当前节点的len,表示更新以 \(ps_i\) 位置结尾的前缀和某个前缀匹配,相同后缀的最大长度是多少。
最后对于一个 \(l\) ,\(r\) 的询问,只要在线段树里找 \(l\) 到 \(r\) 中的max就可以了
#include<bits/stdc++.h>
#define ui unsigned int
#define ll long long
#define db double
#define ld long double
#define ull unsigned long long
const int MAXN=100000+10,MAXM=100000+10;
int n,m,fa[MAXN<<1],len[MAXN<<1],ch[MAXN<<1][2],ans[MAXM],las=1,tot=1,ps[MAXN];
char s[MAXN];
struct node{
int l,r,id;
inline bool operator < (const node &A) const {
return r<A.r||(r==A.r&&l<A.l);
};
};
node query[MAXM];
template<typename T> inline void read(T &x)
{
T data=0,w=1;
char ch=0;
while(ch!='-'&&(ch<'0'||ch>'9'))ch=getchar();
if(ch=='-')w=-1,ch=getchar();
while(ch>='0'&&ch<='9')data=((T)data<<3)+((T)data<<1)+(ch^'0'),ch=getchar();
x=data*w;
}
template<typename T> inline void write(T x,char ch='\0')
{
if(x<0)putchar('-'),x=-x;
if(x>9)write(x/10);
putchar(x%10+'0');
if(ch!='\0')putchar(ch);
}
template<typename T> inline void chkmin(T &x,T y){x=(y<x?y:x);}
template<typename T> inline void chkmax(T &x,T y){x=(y>x?y:x);}
template<typename T> inline T min(T x,T y){return x<y?x:y;}
template<typename T> inline T max(T x,T y){return x>y?x:y;}
#define Mid ((l+r)>>1)
#define ls rt<<1
#define rs rt<<1|1
#define lson ls,l,Mid
#define rson rs,Mid+1,r
struct Segment_Tree{
int Mx[MAXN<<2];
inline void PushUp(int rt)
{
Mx[rt]=max(Mx[ls],Mx[rs]);
}
inline void Update(int rt,int l,int r,int ps,int k)
{
if(l==r&&r==ps)chkmax(Mx[rt],k);
else
{
if(ps<=Mid)Update(lson,ps,k);
else Update(rson,ps,k);
PushUp(rt);
}
}
inline int Query(int rt,int l,int r,int L,int R)
{
if(L<=l&&r<=R)return Mx[rt];
else
{
int res=0;
if(L<=Mid)chkmax(res,Query(lson,L,R));
if(R>Mid)chkmax(res,Query(rson,L,R));
return res;
}
}
};
Segment_Tree sT;
#undef Mid
#undef ls
#undef rs
#undef lson
#undef rson
#define lc(x) ch[(x)][0]
#define rc(x) ch[(x)][1]
struct LinkCut_Tree{
int ch[MAXN<<1][2],fa[MAXN<<1],stack[MAXN<<1],val[MAXN<<1],pd[MAXN<<1],cnt;
inline bool nroot(int x)
{
return lc(fa[x])==x||rc(fa[x])==x;
}
inline void pushdown(int x)
{
if(pd[x])
{
if(lc(x))val[lc(x)]=pd[lc(x)]=pd[x];
if(rc(x))val[rc(x)]=pd[rc(x)]=pd[x];
pd[x]=0;
}
}
inline void rotate(int x)
{
int f=fa[x],p=fa[f],c=(rc(f)==x);
if(nroot(f))ch[p][rc(p)==f]=x;
fa[ch[f][c]=ch[x][c^1]]=f;
fa[ch[x][c^1]=f]=x;
fa[x]=p;
}
inline void splay(int x)
{
cnt=0;
stack[++cnt]=x;
for(register int i=x;nroot(i);i=fa[i])stack[++cnt]=fa[i];
while(cnt)pushdown(stack[cnt--]);
for(register int y=fa[x];nroot(x);rotate(x),y=fa[x])
if(nroot(y))rotate((lc(y)==x)==(lc(fa[y])==y)?y:x);
}
inline void access(int x)
{
for(register int y=0;x;x=fa[y=x])
{
splay(x);
if(val[x])sT.Update(1,1,n,val[x],len[x]);
rc(x)=y;
}
}
};
LinkCut_Tree lT;
#undef lc
#undef rc
inline void extend(int c,int i)
{
int p=las,np=++tot;
las=np;
len[np]=len[p]+1;ps[i]=np;
while(p&&!ch[p][c])ch[p][c]=np,p=fa[p];
if(!p)fa[np]=1;
else
{
int q=ch[p][c];
if(len[q]==len[p]+1)fa[np]=q;
else
{
int nq=++tot;
fa[nq]=fa[q];
memcpy(ch[nq],ch[q],sizeof(ch[nq]));
len[nq]=len[p]+1;fa[q]=fa[np]=nq;
while(p&&ch[p][c]==q)ch[p][c]=nq,p=fa[p];
}
}
}
int main()
{
read(n);read(m);
scanf("%s",s+1);
for(register int i=1;i<=m;++i)
{
read(query[i].l),read(query[i].r);
query[i].id=i;
}
std::sort(query+1,query+m+1);
for(register int i=1,lt=strlen(s+1);i<=lt;++i)extend(s[i]-'0',i);
for(register int i=1;i<=tot;++i)lT.fa[i]=fa[i];
for(register int i=1,j=1;i<=n;++i)
{
lT.access(ps[i]);
while(j<=m&&query[j].r==i)ans[query[j].id]=sT.Query(1,1,n,query[j].l,i),++j;
lT.splay(ps[i]);lT.val[ps[i]]=lT.pd[ps[i]]=i;
}
for(register int i=1;i<=m;++i)write(ans[i],'\n');
return 0;
}
【刷题】LOJ 6041 「雅礼集训 2017 Day7」事情的相似度的更多相关文章
- LOJ #6041. 「雅礼集训 2017 Day7」事情的相似度
我可以大喊一声这就是个套路题吗? 首先看到LCP问题,那么套路的想到SAM(SA的做法也有) LCP的长度是它们在parent树上的LCA(众所周知),所以我们考虑同时统计多个点之间的LCA对 树上问 ...
- loj#6041. 「雅礼集训 2017 Day7」事情的相似度(后缀自动机+启发式合并)
题面 传送门 题解 为什么成天有人想搞些大新闻 这里写的是\(yyb\)巨巨说的启发式合并的做法(虽然\(LCT\)的做法不知道比它快到哪里去了--) 建出\(SAM\),那么两个前缀的最长公共后缀就 ...
- loj#6041. 「雅礼集训 2017 Day7」事情的相似度(SAM set启发式合并 二维数点)
题意 题目链接 Sol 只会后缀数组+暴躁莫队套set\(n \sqrt{n} \log n\)但绝对跑不过去. 正解是SAM + set启发式合并 + 二维数点/ SAM + LCT 但是我只会第一 ...
- LOJ #6041. 「雅礼集训 2017 Day7」事情的相似度 LCT+SAM+线段树
Code: #include<bits/stdc++.h> #define maxn 200003 using namespace std; void setIO(string s) { ...
- #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维护SAM的parent树)
点此看题面 大致题意: 给你一个\(01\)串,每次询问前缀编号在一段区间内的两个前缀的最长公共后缀的长度. 离线存储询问 考虑将询问离线,按右端点大小用邻接表存下来(直接排序当然也可以啦). 这样的 ...
- LOJ6041. 「雅礼集训 2017 Day7」事情的相似度 [后缀树,LCT]
LOJ 思路 建出反串的后缀树,发现询问就是问一个区间的点的\(lca\)的深度最大值. 一种做法是dfs的时候从下往上合并\(endpos\)集合,发现插入一个点的时候只需要把与前驱后继的贡献算进去 ...
随机推荐
- 解决 mysql in 查询排序问题
select id,title from za_item where -- id ,) 返回的结果第一条是对应id是1000,第二条是1003. 如果我们想让结果和in里面的排序一致,可以这么做. s ...
- 日志采集框架 Flume
日志采集框架 Flume 1 概述 Flume是一个分布式.可靠.和高可用的海量日志采集.聚合和传输的系统. Flume可以采集文件,socket数据包等各种形式源数据,又可以将采集到的数据输出到H ...
- 基于testng自动化添加allure报告展示以及jenkins集成
本地执行方式: 1.下载地址 http://allure.qatools.ru/ 2.执行机器添加环境变量 如mac:vi /etc/profile export ALLURE_HOME=/Users ...
- Karma与TSLint
TSLint TSLint是一个可扩展的静态分析工具,用于检查TypeScript代码的可读性,可维护性和功能性错误.收到现代编辑和构建系统的广泛支持,并且可以使用您自己的路由,配置和格式化. 安装 ...
- Tp框架之命名空间
命名空间,相当于虚拟目录 实现自动加载类的机制 初始命名空间:Library文件夹 初始命名空间下面有很多根命名空间: 1.Library里面的文件夹 2.APP的模块文件夹 在tp框架中,只有这两个 ...
- 拼多多商品id怎么查看 拼多多店铺ID怎样看
网上开店平台有很多编号.id等可以区分商品和店铺的标志,拼多多有店铺id也有商品id,这是两个不同的概念,店铺id进入到拼多多店铺即可查询,拼多多商品id怎么查看 拼多多店铺ID怎样看,那么拼多多商品 ...
- Spring Cloud(三):服务提供与调用 Eureka【Finchley 版】
Spring Cloud(三):服务提供与调用 Eureka[Finchley 版] 发表于 2018-04-15 | 更新于 2018-05-07 | 上一篇文章我们介绍了 Eureka 服务 ...
- Machine Learning笔记整理 ------ (一)基本概念
机器学习的定义:假设用P来评估计算机程序在某任务类T上的性能,若一个程序通过利用经验E,使其在T中任务获得了性能改善,我们则说关于任务类T和P,该程序对经验E进行了学习(Mitchell, 1997) ...
- leetcode个人题解——#48 rotage image
思路:本题要求不能利用额外的二维数组实现旋转,所以重点在于弄清矩阵旋转的数学方法. 我的方法是,首先按照副对角线进行对称,然后按照水平中轴线进行对称即可. class Solution { publi ...
- mysql把一字段拆分为多行
sql语句 select a.house_no as '房子',substring_index(substring_index(a.name,',',b.help_topic_id+1),',',-1 ...