LOJ 6041 事情的相似度
LOJ 6041 事情的相似度
题目描述
人的一生不仅要靠自我奋斗,还要考虑到历史的行程。
历史的行程可以抽象成一个 01 串,作为一个年纪比较大的人,你希望从历史的行程中获得一些姿势。
你发现在历史的不同时刻,不断的有相同的事情发生。比如,有两个人同时在世纪之交 11 年的时候上台,同样喜欢与洋人谈笑风生,同样提出了以「三」字开头的理论。
你发现,一件事情可以看成是这个 01 串的一个前缀,这个前缀最右边的位置就是这个事情的结束时间。
两件事情的相似度可以看成,这两个前缀的最长公共后缀长度。
现在你很好奇,在一段区间内结束的事情中最相似的两件事情的相似度是多少呢?
数据范围:\(n,m\le 10^5\)
思路
首先,建一个后缀自动机。
那么,两件事情的相似度就是他们\(parent\)树上的最近公共祖先的深度。
我们考虑把所有询问离线下来,按照右端点排序。
那么,每次查询时,我们只需要知道\([l,r]\)中每个点到他后面的点的贡献,再取\(max\)即可
我们考虑,每次加入一个点的时候,把这个点到根的路径全部染上他的颜色。
每遇见一种颜色时,更新这个颜色的答案。
首先发现,如此操作与我们理论上要求的略有不同,因为后面的颜色会覆盖前面的颜色,就导致前面的颜色的答案并没有更新。
但是再仔细想想,这对答案并不影响。因为我们是从前到后把点加进来的,所以说如果你能覆盖前面的颜色,也一定能覆盖后面的颜色。
于是我们用类似\(LCT\)的\(access\)操作维护即可。
代码
#include<bits/stdc++.h>
#define lch ch[x][0]
#define rch ch[x][1]
#define now c[top].nxt[k]
using namespace std;
const int sz=2e5+7;
int n,m;
int cnt,L,R;
int rt,lst,cur,top;
int f[sz],col[sz];
int pos[sz];
int mx[sz],ans[sz];
int ch[sz][2];
char s[sz];
struct node{
int len,link,nxt[2];
}c[sz];
struct Que{
int l,r,id;
const bool operator <(const Que &p)const{
return r<p.r;
}
}q[sz];
void update(int x,int sum){
for(;x;x-=x&-x) mx[x]=max(mx[x],sum);
}
int query(int x){
int ret=0;
for(;x<=n;x+=x&-x) ret=max(ret,mx[x]);
return ret;
}
bool nroot(int x){
return ch[f[x]][0]==x||ch[f[x]][1]==x;
}
int get(int x){
return ch[f[x]][1]==x;
}
void pushdn(int x){
if(lch) col[lch]=col[x];
if(rch) col[rch]=col[x];
}
void pushall(int x){
if(nroot(x)) pushall(f[x]);
pushdn(x);
}
void rotate(int x){
int y=f[x],z=f[y],k=get(x),w=ch[x][!k];
if(nroot(y)) ch[z][get(y)]=x;ch[x][!k]=y;ch[y][k]=w;
if(w) f[w]=y;f[y]=x;f[x]=z;
}
void splay(int x){
pushall(x);
for(int y;nroot(x);rotate(x)) if(nroot(y=f[x])) rotate(get(x)^get(y)?x:y);
}
void access(int x,int cc){
for(int y=0;x;x=f[y=x]){
splay(x);
update(col[x],c[x].len);
col[x]=cc;
rch=y;
}
}
void insert(int k){
cur=++cnt;
c[cur].len=c[lst].len+1;
pos[c[cur].len]=cur;
top=lst;
lst=cur;
for(;top&&!now;top=c[top].link) now=cur;
if(!top) return (void)(c[cur].link=rt);
if(c[top].len+1==c[now].len) return (void)(c[cur].link=now);
int p=++cnt,np=now;
c[p]=c[np];
c[p].len=c[top].len+1;
c[np].link=c[cur].link=p;
for(;top&&now==np;top=c[top].link) now=p;
}
void build(){
for(int i=cnt;i>=2;i--) f[i]=c[i].link;
}
int main(){
scanf("%d%d",&n,&m);
scanf("%s",s+1);
rt=lst=++cnt;
for(int i=1;i<=n;i++) insert(s[i]-'0');
build();
for(int i=1;i<=m;i++){
scanf("%d%d",&L,&R);
q[i]=(Que){L,R,i};
}
sort(q+1,q+m+1);
int tot=0;
for(int i=1;i<=m;i++){
while(tot<q[i].r){
tot++;
access(pos[tot],tot);
}
ans[q[i].id]=query(q[i].l);
}
for(int i=1;i<=m;i++) printf("%d\n",ans[i]);
}
以仁心说,以学心听,以公心辩