loj

description

给你一个字符串和一个数组\(w_i\),定义\(\mbox{LCP}(i,j)\)为\(i,j\)两个后缀的最长公共前缀。求\(\max_{i,j}\mbox{LCP}(i,j)+(w_i\ \mbox{xor}\ w_j)\)。

\(n \le 10^5\)

sol

首先,\(\mbox{LCP}(i,j)\)是后缀排序上的一段连续区间的\(\mbox{Height}\)最小值。

那么我们可以枚举这个最小值出现的位置,那么跨越这个位置的所有点对的\(\mbox{LCP}\)就确定了。

接下来我们只要考虑最大化\(w_i\ \mbox{xor}\ w_j\)。

显然可以用可持久化\(\mbox{Tire}\)树实现,复杂度是查询次数\(\times \log n\)的。

算法流程大致是这样的:先找到\(\mbox{Height}\)最小的位置\(p\),计算所有跨越\(p\)的点对的答案。枚举分割点两侧\(size\)较小的一侧,在可持久化\(\mbox{Tire}\)树上查询它和另一侧异或的最大值。接着两侧被完全割裂开,可以分别递归下去处理。

考虑这个东西的时间复杂度。可以发现这就是一个倒着做的启发式合并,所以复杂度就是\(O(n\log^2n)\)。

当然也可以按\(\mbox{Height}\)从大到小启发式合并,复杂度是一样的。

code

#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
int gi(){
int x=0,w=1;char ch=getchar();
while ((ch<'0'||ch>'9')&&ch!='-') ch=getchar();
if (ch=='-') w=0,ch=getchar();
while (ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+ch-'0',ch=getchar();
return w?x:-x;
}
const int N = 1e5+5;
struct node{int ch[2],sz;}tr[N*35];
int n,t[N],x[N],y[N],SA[N],Rank[N],Height[N],st[19][N],lg[N],rt[N],tot,w[N],ans;
char s[N];
bool cmp(int i,int j,int k){
return y[i]==y[j]&&y[i+k]==y[j+k];
}
void getSA(){
int m=30;
for (int i=1;i<=n;++i) ++t[x[i]=s[i]-'a'+1];
for (int i=1;i<=m;++i) t[i]+=t[i-1];
for (int i=n;i;--i) SA[t[x[i]]--]=i;
for (int k=1;k<=n;k<<=1){
int p=0;
for (int i=0;i<=m;++i) y[i]=0;
for (int i=n-k+1;i<=n;++i) y[++p]=i;
for (int i=1;i<=n;++i) if (SA[i]>k) y[++p]=SA[i]-k;
for (int i=0;i<=m;++i) t[i]=0;
for (int i=1;i<=n;++i) ++t[x[y[i]]];
for (int i=1;i<=m;++i) t[i]+=t[i-1];
for (int i=n;i;--i) SA[t[x[y[i]]]--]=y[i];
swap(x,y);x[SA[1]]=p=1;
for (int i=2;i<=n;++i) x[SA[i]]=cmp(SA[i],SA[i-1],k)?p:++p;
if (p>=n) break;m=p;
}
for (int i=1;i<=n;++i) Rank[SA[i]]=i;
for (int i=1,j=0;i<=n;++i){
if (j) --j;
while (s[i+j]==s[SA[Rank[i]-1]+j]) ++j;
Height[Rank[i]]=j;st[0][i]=i;
}
for (int i=2;i<=n;++i) lg[i]=lg[i>>1]+1;
for (int j=1;j<=lg[n];++j)
for (int i=1;i+(1<<j)-1<=n;++i)
if (Height[st[j-1][i]]<Height[st[j-1][i+(1<<j-1)]])
st[j][i]=st[j-1][i];
else st[j][i]=st[j-1][i+(1<<j-1)];
}
int cal(int l,int r){
int k=lg[r-l+1];
if (Height[st[k][l]]<Height[st[k][r-(1<<k)+1]]) return st[k][l];
return st[k][r-(1<<k)+1];
}
void modify(int &x,int p,int dep){
tr[++tot]=tr[x];++tr[x=tot].sz;
if (!~dep) return;
modify(tr[x].ch[(p>>dep)&1],p,dep-1);
}
int query(int x,int y,int p,int dep){
if (!~dep) return 0;
int c=(p>>dep)&1;c^=1;
if (tr[tr[x].ch[c]].sz-tr[tr[y].ch[c]].sz) return query(tr[x].ch[c],tr[y].ch[c],p,dep-1)|(1<<dep);
c^=1;return query(tr[x].ch[c],tr[y].ch[c],p,dep-1);
}
void solve(int l,int r){
if (l==r) return;int mid=cal(l+1,r)-1;
if (mid-l+1<=r-mid){
for (int i=l;i<=mid;++i)
ans=max(ans,Height[mid+1]+query(rt[r],rt[mid],w[SA[i]],18));
}else{
for (int i=mid+1;i<=r;++i)
ans=max(ans,Height[mid+1]+query(rt[mid],rt[l-1],w[SA[i]],18));
}
solve(l,mid);solve(mid+1,r);
}
int main(){
n=gi();scanf("%s",s+1);getSA();
for (int i=1;i<=n;++i) w[i]=gi();
for (int i=1;i<=n;++i) modify(rt[i]=rt[i-1],w[SA[i]],18);
solve(1,n);printf("%d\n",ans);return 0;
}
05-11 11:09