题意:
给你一个字符串,每个位置有一个权值(可正可负),对于每一个
i
∈
[
0
,
n
−
1
]
i\in[0,n-1]
i∈[0,n−1],求所有lcp长度为
i
i
i的后缀的对数,并且求每一对lcp为
i
i
i的后缀的两个权值相乘的最大值。长度为
i
i
i的lcp也可以算做长度是
[
0
−
i
−
1
]
[0-i-1]
[0−i−1]。
n
<
=
1
e
5
n<=1e5
n<=1e5。
题解:
怎么说呢,可能我还是后缀数组学的太差了吧。本来是想找后缀数组的题找到的这个题,但是我只会用后缀数组搞给定一个确定的
i
i
i的答案。
怎么想都是用SAM做,因为建出SAM之后,我们可以用parent树来算答案,我们可以进行树形dp,这样我们在每个点找出它会在哪个深度产生贡献就可以了。具体来说,我们要在树形dp的时候维护最大值、最小值和子树内原串的后缀的个数。SAM上的一个点表示的是 [ l e n [ f a [ i ] ] + 1 , l e n [ i ] ] [len[fa[i]]+1,len[i]] [len[fa[i]]+1,len[i]]的这么多个串的答案,但是由于长度长的答案可以加到长度更短里,所以我们只更新长度是 l e n [ i ] len[i] len[i]的答案,最后再取一个长度的后缀和就好了,维护最大值的就取个后缀长度的最大值。
如果说的不明白,看一眼代码也就知道在干什么了。
代码:
#include <bits/stdc++.h>
using namespace std;
int n,len[600010],ch[600010][26],fa[600010],hed[600010];
int rt=1,lst=1,cnt=1,num;
long long sz[600010],val[600010],mn[600010],mx[600010],ans1[300010],ans2[300010];
char s[300010];
const long long inf=1000000000000000000;
struct node
{
int to,next;
}a[600010];
inline int read()
{
int x=0,f=1;
char s=getchar();
while(s>'9'||s<'0')
{
if(s=='-')
f=-1;
s=getchar();
}
while(s>='0'&&s<='9')
{
x=x*10+s-'0';
s=getchar();
}
return x*f;
}
inline void insert(int x,long long y)
{
int cur=++cnt,pre=lst;
lst=cur;
len[cur]=len[pre]+1;
sz[cur]=1;
mn[cur]=mx[cur]=y;
for(;pre&&!ch[pre][x];pre=fa[pre])
ch[pre][x]=cur;
if(!pre)
fa[cur]=rt;
else
{
int ji=ch[pre][x];
if(len[ji]==len[pre]+1)
fa[cur]=ji;
else
{
int gg=++cnt;
len[gg]=len[pre]+1;
memcpy(ch[gg],ch[ji],sizeof(ch[ji]));
fa[gg]=fa[ji];
fa[ji]=fa[cur]=gg;
for(;pre&&ch[pre][x]==ji;pre=fa[pre])
ch[pre][x]=gg;
}
}
}
inline void add(int from,int to)
{
a[++num].to=to;
a[num].next=hed[from];
hed[from]=num;
}
inline void dfs(int x)
{
if(mn[x]==0&&mx[x]==0)
{
mn[x]=inf+1;
mx[x]=-inf-1;
}
for(int i=hed[x];i;i=a[i].next)
{
int y=a[i].to;
dfs(y);
if((mn[x]<=inf&&mx[x]>=-inf)&&(mn[y]<=inf&&mx[y]>=-inf))
ans2[len[x]]=max(ans2[len[x]],max(mx[x]*mx[y],mn[y]*mn[x]));
ans1[len[x]]+=sz[x]*sz[y];
mx[x]=max(mx[x],mx[y]);
mn[x]=min(mn[x],mn[y]);
sz[x]+=sz[y];
}
}
int main()
{
n=read();
scanf("%s",s+1);
for(int i=1;i<=n;++i)
val[i]=read();
for(int i=n;i>=1;--i)
insert(s[i]-'a',val[i]);
for(int i=2;i<=cnt;++i)
add(fa[i],i);
for(int i=n;i>=0;--i)
ans2[i]=-inf-1;
dfs(1);
for(int i=n-1;i>=0;--i)
{
ans2[i]=max(ans2[i],ans2[i+1]);
ans1[i]+=ans1[i+1];
}
for(int i=0;i<=n-1;++i)
{
if(ans1[i])
printf("%lld %lld\n",ans1[i],ans2[i]);
else
printf("0 0\n");
}
return 0;
}