这回换了个SAM写,SA的做法见:portal
一开始以为可以做到线性…结果写了之后发现还是要挂棵线段树…
我们考虑对反串建SAM,搞出后缀树,这样任意两个后缀的lca就是他们的lcp。于是我们可以在每个lca处统计答案。
不过一个点可能会覆盖一段长度区间,所以要区间修改答案。
复杂度
O(26n+nlogn)
O
(
26
n
+
n
l
o
g
n
)
#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define inf 1e18
#define N 300010
inline char gc(){
static char buf[1<<16],*S,*T;
if(S==T){T=(S=buf)+fread(buf,1,1<<16,stdin);if(T==S) return EOF;}
return *S++;
}
inline int read(){
int x=0,f=1;char ch=gc();
while(ch<'0'||ch>'9'){if(ch=='-') f=-1;ch=gc();}
while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=gc();
return x*f;
}
int n=0,m=0,a[N],par[N<<1],mx[N<<1],sz[N<<1],mx1[N<<1],mx2[N<<1],mn1[N<<1],mn2[N<<1],son[N<<1][26],rt=0,lst=0,cnt[N],q[N<<1];
char s[N];ll ans1[N],ans2[N];
struct node{
ll ans1,ans2;
}tr[N<<2];
inline void get_S(){
char ch=gc();
while(ch<'a'||ch>'z') ch=gc();
while(ch>='a'&&ch<='z') s[++m]=ch,ch=gc();
}
inline int newp(){
++n;mx1[n]=mx2[n]=-1;mn1[n]=mn2[n]=1;return n;
}
inline void extend(int ch,int val){
int p=lst,np=newp();mx[np]=mx[p]+1;lst=np;sz[np]=1;if(val>=0) mx1[np]=val;else mn1[np]=val;
for(;p&&!son[p][ch];p=par[p]) son[p][ch]=np;
if(!p){par[np]=rt;return;}
int q=son[p][ch];
if(mx[q]==mx[p]+1){par[np]=q;return;}
int nq=newp();mx[nq]=mx[p]+1;memcpy(son[nq],son[q],sizeof(son[q]));
par[nq]=par[q];par[q]=par[np]=nq;
for(;p&&son[p][ch]==q;p=par[p]) son[p][ch]=nq;
}
inline void upd(int p,int val){
if(val>mx1[p]) mx2[p]=mx1[p],mx1[p]=val;
else if(val>mx2[p]) mx2[p]=val;
}
inline void upd1(int p,int val){
if(val<mn1[p]) mn2[p]=mn1[p],mn1[p]=val;
else if(val<mn2[p]) mn2[p]=val;
}
inline void change(int p,int l,int r,int x,int y,ll val1,ll val2){
if(x<=l&&r<=y){tr[p].ans1+=val1;tr[p].ans2=max(tr[p].ans2,val2);return;}
int mid=l+r>>1;
if(x<=mid) change(p<<1,l,mid,x,y,val1,val2);
if(y>mid) change(p<<1|1,mid+1,r,x,y,val1,val2);
}
inline void dfs(int p,int l,int r){
if(l==r){ans1[l]=tr[p].ans1;ans2[l]=ans1[l]?tr[p].ans2:0;return;}
int mid=l+r>>1;tr[p<<1].ans1+=tr[p].ans1;tr[p<<1].ans2=max(tr[p<<1].ans2,tr[p].ans2);
tr[p<<1|1].ans1+=tr[p].ans1;tr[p<<1|1].ans2=max(tr[p<<1|1].ans2,tr[p].ans2);
dfs(p<<1,l,mid);dfs(p<<1|1,mid+1,r);
}
inline void build(int p,int l,int r){
tr[p].ans2=-inf;if(l==r) return;int mid=l+r>>1;
build(p<<1,l,mid);build(p<<1|1,mid+1,r);
}
int main(){
// freopen("savour14.in","r",stdin);
// freopen("a.out","w",stdout);
read();get_S();for(int i=1;i<=m;++i) a[i]=read();rt=lst=newp();
for(int i=m;i>=1;--i) extend(s[i]-'a',a[i]);
for(int i=1;i<=n;++i) cnt[mx[i]]++;
for(int i=1;i<=m;++i) cnt[i]+=cnt[i-1];
for(int i=n;i>=1;--i) q[cnt[mx[i]]--]=i;mx[0]=-1;build(1,1,m);
for(int i=n;i>=1;--i){
int x=q[i];sz[par[x]]+=sz[x];
upd(par[x],mx1[x]);upd(par[x],mx2[x]);upd1(par[x],mn1[x]);upd1(par[x],mn2[x]);
if(sz[x]==1) continue;ll res;if(mx2[x]==-1&&mn2[x]==1) res=(ll)mx1[x]*mn1[x];
else res=max(mx2[x]==-1?0:(ll)mx1[x]*mx2[x],mn2[x]==1?0:(ll)mn1[x]*mn2[x]);
change(1,1,m,mx[par[x]]+1+1,mx[x]+1,(ll)sz[x]*(sz[x]-1)>>1,res);
}dfs(1,1,m);
for(int i=1;i<=m;++i) printf("%lld %lld\n",ans1[i],ans2[i]);
return 0;
}