LOJ 6041 事情的相似度

六月ゝ 毕业季﹏ 提交于 2019-12-06 09:35:56

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]);
} 
标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!