LOJ6041 「雅礼集训 2017 Day7」事情的相似度
题目传送门
分析:
前缀的公共后缀,那直接SAM安排上
两个前缀的公共后缀为两个串对应Parent树上节点的LCA
于是\([l,r]\)之间的公共后缀最长对应这些点两两在Parent树上LCA的最大的len
Set启发式合并做法:
建出了Parent树,我们考虑每一个点\(u\),合并它的子树集合时,两个集合中各取一个点的LCA一定是\(u\)
我们可以找到若干对点ID相近且LCA为\(u\),他们会对答案造成贡献,而每次合并点集,产生的对数为较小的集合的大小
使用启发式合并,空间复杂度是\(O(nlogn)\),时间复杂度\(O(nlog^{2}n)\)
点对预处理出来之后,可以离线+树状数组查询答案
代码可以看yyb这篇博客,很好理解
LCT+树状数组做法:
我们将询问按\(r\)离线,每次按顺序加入一个点\(i\)时,我们将这个点到根的路径全部染成颜色\(i\),而某个点\(u\)原来的颜色\(j\)被覆盖,那么\(u\)就是\(i,j\)的LCA
每个点的颜色都是最近被覆盖的,得到的点对与上面Set做法得到的点对是一样的
到根路径染色实际上是LCT的access过程,于是直接用LCT维护,过程中即可更新树状数组求答案
时间复杂度\(O(nlog^{2}n)\)
代码是第二种做法的:
#include<cstdio>
#include<cstring>
#include<cmath>
#include<queue>
#include<algorithm>
#include<queue>
#include<map>
#define maxn 400005
#define INF 0x3f3f3f3f
using namespace std;
inline int getint()
{
int num=0,flag=1;char c;
while((c=getchar())<'0'||c>'9')if(c=='-')flag=-1;
while(c>='0'&&c<='9')num=num*10+c-48,c=getchar();
return num*flag;
}
int n,m;
int Id[maxn];
struct node{
int fa,nxt[2],len;
}t[maxn];
int lst,tot;
int ch[maxn][2],val[maxn],tag[maxn],fa[maxn];
vector<int>q[maxn];
int p[maxn];
int stk[maxn],tp;
int mx[maxn],ans[maxn];
char s[maxn];
inline void insert(int c)
{
int p=lst,np=lst=++tot;
t[np].len=t[p].len+1;
while(p&&!t[p].nxt[c])t[p].nxt[c]=np,p=t[p].fa;
if(!p)t[np].fa=1;
else
{
int q=t[p].nxt[c];
if(t[q].len==t[p].len+1)t[np].fa=q;
else
{
int nq=++tot;
memcpy(t[nq].nxt,t[q].nxt,sizeof t[q].nxt);
t[nq].len=t[p].len+1;
t[nq].fa=t[q].fa;
t[q].fa=t[np].fa=nq;
while(p&&t[p].nxt[c]==q)t[p].nxt[c]=nq,p=t[p].fa;
}
}
}
inline void update(int x,int num)
{for(int i=n-x+1;i<=n;i+=i&(-i))mx[i]=max(mx[i],num);}
inline int query(int x)
{int num=0;for(int i=n-x+1;i;i-=i&(-i))num=max(num,mx[i]);return num;}
inline bool isroot(int x)
{return ch[fa[x]][0]!=x&&ch[fa[x]][1]!=x;}
inline void change(int x,int num){val[x]=tag[x]=num;}
inline void pushdown(int x)
{
if(!tag[x])return;
if(ch[x][0])change(ch[x][0],tag[x]);
if(ch[x][1])change(ch[x][1],tag[x]);
tag[x]=0;
}
inline void rotate(int x)
{
int y=fa[x],z=fa[y];
int l=(ch[y][1]==x),r=l^1;
if(!isroot(y))
{
if(ch[z][0]==y)ch[z][0]=x;else ch[z][1]=x;
}
fa[x]=z,fa[y]=x,ch[y][l]=ch[x][r],fa[ch[x][r]]=y,ch[x][r]=y;
}
inline void splay(int x)
{
stk[++tp]=x;for(int i=x;!isroot(i);i=fa[i])stk[++tp]=fa[i];
while(tp)pushdown(stk[tp--]);
while(!isroot(x))
{
int y=fa[x],z=fa[y];
if(!isroot(y))
{
if((ch[z][0]==y)^(ch[y][0]==x))rotate(x);
else rotate(y);
}
rotate(x);
}
}
inline void access(int x,int num)
{
int tmp=0;
for(;x;ch[x][1]=tmp,tmp=x,x=fa[x])
splay(x),update(val[x],t[x].len);
change(tmp,num);
}
int main()
{
n=getint(),m=getint();
scanf("%s",s+1);
for(int i=1;i<=m;i++)p[i]=getint(),q[getint()].push_back(i);
lst=tot=1;
for(int i=1;i<=n;i++)insert(s[i]-'0'),Id[i]=lst;
for(int i=1;i<=tot;i++)fa[i]=t[i].fa;
for(int i=1;i<=n;i++)
{
access(Id[i],i);
int sz=q[i].size();
for(int j=0;j<sz;j++)ans[q[i][j]]=query(p[q[i][j]]);
}
for(int i=1;i<=m;i++)printf("%d\n",ans[i]);
}