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]); }