事情的相似度
题解
挺好想的一道题。
相当于要求一个区间的前缀的最长公共后缀。应该很容易就能想到后缀自动机。貌似有人用LCT做
我们可以先将SAM建出来,而两段后缀的lcs就是它们的lca的深度。
我们要求的相当于编号在一个区间中的点的lca的最大深度。
考虑启发式合并,对于点
u
u
u的子树集合
S
u
S_{u}
Su与它的儿子
v
v
v的子树集合
S
v
S_{v}
Sv,我们在将它们合并在一起时,可以知道在这两个集合中的点互相匹配,它们的lcs就是
l
e
n
u
len_{u}
lenu。
如果将这些点对一一记录下来会达到
n
2
n^2
n2级别,无疑是在寻死。
考虑那些点会真正用到,假设我们现在在为
u
u
u匹配点对,可能最优匹配对象的
(
v
,
l
e
n
l
c
a
(
u
,
v
)
)
(v,len_{lca(u,v)})
(v,lenlca(u,v))明显会构成一个凸包,所以我们在每个点匹配时,都只需要匹配它两侧的点。
将所有匹配了的点对
(
u
,
v
)
(u,v)
(u,v)放在坐标系上,该点的权值为
l
e
n
l
c
a
(
u
,
v
)
len_{lca(u,v)}
lenlca(u,v)。
注意,记录下的点必须是横坐标大于纵坐标,这样才能保证下面扫描线的正确性。
我们需要找的就是坐标系
(
l
,
l
)
(l,l)
(l,l)到
(
r
,
r
)
(r,r)
(r,r)两点之间构成的正方形中所覆盖到的最大点权。
这可以将所有询问离线下来后用扫描先来处理。
我们就从
1
1
1到
n
n
n将横坐标为
i
i
i的点放上去,这个可以用树状数组来维护。
对于右端点在
i
i
i的询问,我们就在树状数组上查询后缀的最大值即可。
总时间复杂度 O ( 我 不 会 算 ) O\left(我不会算\right) O(我不会算),但还是可以过。
源码
#include<cstdio>
#include<cmath>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<vector>
#include<queue>
#include<map>
#include<set>
using namespace std;
#define MAXN 200005
#define lowbit(x) (x&-x)
#define reg register
typedef long long LL;
typedef unsigned long long uLL;
typedef pair<int,int> pii;
const int INF=0x7f7f7f7f;
template<typename _T>
_T Fabs(_T x){return x<0?-x:x;}
template<typename _T>
void read(_T &x){
_T f=1;x=0;char s=getchar();
while(s>'9'||s<'0'){if(s=='-')f=-1;s=getchar();}
while('0'<=s&&s<='9'){x=(x<<3)+(x<<1)+(s^48);s=getchar();}
x*=f;
}
int n,m,b[MAXN],id[MAXN],tr[MAXN],ans[MAXN];
set<int> s[MAXN];vector<pii>G[MAXN],q[MAXN];
void insert(int x,int y){x=n-x+1;while(x<=n)tr[x]=max(tr[x],y),x+=lowbit(x);}
int query(int x){x=n-x+1;int res=0;while(x)res=max(tr[x],res),x-=lowbit(x);return res;}
struct ming{int ch[2],len,fa;};
class SAM{
private:
ming a[MAXN];int tot,las,t[MAXN],ord[MAXN],ist[MAXN];
public:
void init(){las=++tot;}
void extend(int x,int ai){
int p=las,np=las=++tot;a[np].len=a[p].len+1;s[np].insert(ai);
for(;p&&!a[p].ch[x];p=a[p].fa)a[p].ch[x]=np;
if(!p){a[np].fa=1;return ;}int q=a[p].ch[x];
if(a[q].len==a[p].len+1){a[np].fa=q;return ;}
int nq=++tot;a[nq]=a[q];a[nq].len=a[p].len+1;
a[q].fa=a[np].fa=nq;for(;p&&a[p].ch[x]==q;p=a[p].fa)a[p].ch[x]=nq;
}
void solve(){
for(int i=1;i<=tot;i++)t[a[i].len]++;
for(int i=1;i<=n;i++)t[i]+=t[i-1];
for(int i=1;i<=tot;i++)id[i]=t[a[i].len]--;
for(int i=1;i<=tot;i++)ord[id[i]]=i,ist[i]=i;
for(int i=tot;i>1;i--){
int u=ord[i];if(s[ist[u]].size()>s[ist[a[u].fa]].size())swap(ist[u],ist[a[u].fa]);
for(auto k:s[ist[u]]){
set<int>::iterator it=s[ist[a[u].fa]].lower_bound(k);
if(it!=s[ist[a[u].fa]].end())G[*it].push_back(make_pair(a[a[u].fa].len,k));
if(it!=s[ist[a[u].fa]].begin())G[k].push_back(make_pair(a[a[u].fa].len,*--it));
}
for(auto k:s[ist[u]])s[ist[a[u].fa]].insert(k);s[ist[u]].clear();
}
}
}T;
signed main(){
read(n);read(m);T.init();
for(int i=1;i<=n;i++)scanf("%1d",&b[i]),T.extend(b[i],i);T.solve();
for(int i=1;i<=m;i++){
int l,r;read(l);read(r);
q[r].push_back(make_pair(l,i));
}
for(int i=1;i<=n;i++){
for(auto j:G[i])insert(j.second,j.first);
for(auto j:q[i])ans[j.second]=query(j.first);
}
for(int i=1;i<=m;i++)printf("%d\n",ans[i]);
return 0;
}