很显然可以用后缀自动机来搞。
将输入的字符串翻转,构造SAM。
对每一个节点x,求出:
(1)子树中 满足LCA(u,v)==x 的点对 的对数
(2)子树中 满足LCA(u,v)==x 的点对 的美味值乘积最大值。
注意最大值有可能由两个最小的负数相乘得到,所以最大、最小值都要记。
最后的答案为ans1[],ans2[],如果节点x表示的最长子串长度为Max,
那么x的答案可以更新ans1[Max],ans2[Max].
注意数据范围,INF要足够大。
代码:
#include<cstdio>
#include<iostream>
#include<cstdlib>
#include<cmath>
#include<cstring>
#define LL long long
using namespace std;
const int maxn=600000+5;
const LL inf= 1000000000000000000LL+5LL;
int tot=1,last=1,n,x[maxn],dfn[maxn];
LL A[maxn], ans1[maxn],ans2[maxn];
char s[maxn];
struct node{
int Next[26];
int Max,pre;
LL maxv,minv,cnt,multi,size;
// maxv,minv表示子树中美味值得最大、最小值
// cnt 表示有多少对 节点(u,v)满足LCA(u,v)为当前节点.
//multi 表示满足条件的 (u,v)的最大美味值。
}T[maxn];
template <typename T>
inline void _read(T &x){
char ch=getchar(); bool mark=false;
for(;!isdigit(ch);ch=getchar())if(ch=='-')mark=true;
for(x=0;isdigit(ch);ch=getchar())x=x*10+ch-'0';
if(mark)x=-x;
}
void Insert(int x){
int id= s[x]-'a';
int np= ++tot,cur=last;
T[np].Max=T[last].Max+1; T[np].size=1;
T[np].maxv=T[np].minv=A[x];
T[np].multi =-inf;
while(cur){
if(!T[cur].Next[id]) T[cur].Next[id]= np;
else {
int v= T[cur].Next[id];
if(T[v].Max==T[cur].Max+1) T[np].pre=v;
else{
int nq= ++tot;
memcpy(T[nq].Next,T[v].Next,sizeof(T[v].Next));
T[nq].maxv=T[nq].multi= -inf;
T[nq].minv= inf;
T[nq].Max=T[cur].Max+1;
T[nq].pre=T[v].pre;
T[np].pre= nq; T[v].pre= nq;
for(int i= cur;T[i].Next[id]==v;i=T[i].pre)
T[i].Next[id]=nq;
}
break;
}
cur=T[cur].pre;
}
if(!T[np].pre)T[np].pre=1;
last=np;
}
void TreeDP(){
int i,j;
T[0].multi=T[1].maxv=T[1].multi= -inf; T[1].minv= inf;
for(i=1;i<=tot;i++)x[T[i].Max]++;
for(i=1;i<=n;i++) x[i]+=x[i-1];
for(i=1;i<=tot;i++) dfn[x[T[i].Max]--]= i;
for(i=tot;i>0;i--){ //深度由深到浅 DP
int cur= dfn[i],fa=T[cur].pre;
T[fa].cnt+= T[fa].size*T[cur].size;
T[fa].size+= T[cur].size;
if(T[fa].maxv!= -inf) T[fa].multi= max(T[fa].multi,T[fa].maxv*T[cur].maxv);
if(T[fa].minv!= inf) T[fa].multi= max(T[fa].multi,T[fa].minv*T[cur].minv);
//这里不判断乘法会溢出
T[fa].maxv=max(T[fa].maxv,T[cur].maxv);
T[fa].minv=min(T[fa].minv,T[cur].minv);
}
}
int main(){
int i,j;
_read(n);
scanf("%s",s+1);
for(i=1;i<=n;i++)_read(A[i]);
for(i=n;i>0;i--) Insert(i);
TreeDP();
for(i=0;i<n;i++)ans2[i]= -inf;
for(i=1;i<=tot;i++)
ans1[T[i].Max]+=T[i].cnt,
ans2[T[i].Max]=max(T[i].multi,ans2[T[i].Max]);
for(i=n-2;i>=0;i--){
ans1[i]+=ans1[i+1];
if(ans1[i+1]) ans2[i]=max(ans2[i],ans2[i+1]);
}
for(i=0;i<n;i++)
printf("%lld %lld\n",ans1[i], ans1[i]? ans2[i]:0);
return 0;
}