后缀自动机万岁!
一、题目
二、解法
把题目的问题稍作转化,我们可以求出有多少对后缀满足 l c p ≥ i lcp\geq i lcp≥i和求出这些对中乘积的最大值,我们只需要求出 l c p = i lcp=i lcp=i的值然后后缀和统计即可。
建出后缀自动机(由于权值应该存在结束的地方,所以我们需要把输入的串反过来建),考虑在后缀自动机上 d p dp dp,两个后缀的 l c p lcp lcp就是 p a r e n t parent parent树上的 l c a lca lca,我们维护子树标记点数量 s i z siz siz,最大值和次大值,最小值和次小值,因为权值中有负数,所以都要考虑。
然后在 d p dp dp的时候就把答案给存下来, d p dp dp过程就不讲了,如果 s i z < 2 siz<2 siz<2的话就直接退出来。
时间复杂度 O ( n ) O(n) O(n),还有一些易错点(标记了注释)和细节,可以参考我的代码。
#include <cstdio>
#include <cstring>
#include <iostream>
#define int long long
#define inf (1e9+7)
using namespace std;
const int M = 600005;
int read()
{
int x=0,flag=1;char c;
while((c=getchar())<'0' || c>'9') if(c=='-') flag=-1;
while(c>='0' && c<='9') x=(x<<3)+(x<<1)+(c^48),c=getchar();
return x*flag;
}
int n,w[M];char s[M];
struct node
{
int len,fa,ch[26];
node() {memset(ch,0,sizeof ch);len=fa=0;}
};
struct edge
{
int v,next;
edge(int V=0,int N=0) : v(V) , next(N) {}
};
struct automaton
{
int cnt,last,mx[M],mx2[M],mi[M],mi2[M];node a[M];
int tot,siz[M],sum[M],ans[M],f[M];edge e[2*M];
automaton() {cnt=last=1;}
void add(int c,int w)
{
int p=last,np=last=++cnt;siz[np]=1;
mx[np]=mi[np]=w;mx2[np]=-inf;mi2[np]=inf;
a[np].len=a[p].len+1;
for(;p && !a[p].ch[c];p=a[p].fa) a[p].ch[c]=np;
if(!p) a[np].fa=1;
else
{
int q=a[p].ch[c];
if(a[q].len==a[p].len+1) a[np].fa=q;
else
{
int nq=++cnt;mx[nq]=mx2[nq]=-inf;mi[nq]=mi[nq]=inf;
//一定要赋初值,卡了我好久,注意注意!!
a[nq]=a[q];a[nq].len=a[p].len+1;
a[q].fa=a[np].fa=nq;
for(;p && a[p].ch[c]==q;p=a[p].fa) a[p].ch[c]=nq;
}
}
}
void link(int u,int v)
{
e[++tot]=edge(v,f[u]),f[u]=tot;
e[++tot]=edge(u,f[v]),f[v]=tot;
}
void upx(int u,int v)
{
if(mx[u]<v)
{
mx2[u]=mx[u];
mx[u]=v;
}
else if(mx2[u]<v)
mx2[u]=v;
}
void upn(int u,int v)
{
if(mi[u]>v)
{
mi2[u]=mi[u];
mi[u]=v;
}
else if(mi2[u]>v)
mi2[u]=v;
}
void dfs(int u)
{
int len=a[u].len,fa=a[u].fa,s=siz[u];
for(int i=f[u];i;i=e[i].next)
{
int v=e[i].v;
if(v==fa) continue;
dfs(v);
upx(u,mx[v]);upx(u,mx2[v]);
upn(u,mi[v]);upn(u,mi2[v]);
s+=siz[v];
}
if(s<2) {siz[u]=s;return ;}//赋值一下严谨些吧qwq,虽然没有卡这个点
ans[len]=max(ans[len],max(mi[u]*mi2[u],mx[u]*mx2[u]));
for(int i=f[u];i;i=e[i].next)
{
int v=e[i].v;
if(v==fa) continue;
sum[len]+=siz[u]*siz[v];
siz[u]+=siz[v];
}
}
void solve()
{
memset(ans,-63,sizeof ans);
mx[1]=mx2[1]=-inf;mi[1]=mi2[1]=inf;
for(int i=2;i<=cnt;i++) link(i,a[i].fa);
dfs(1);
for(int i=n-1;i>=0;i--)
sum[i]+=sum[i+1],ans[i]=max(ans[i],ans[i+1]);
for(int i=0;i<n;i++)
printf("%lld %lld\n",sum[i],!sum[i]?0ll:ans[i]);
}
}Sam;
signed main()
{
n=read();
scanf("%s",s);
for(int i=0;i<n;i++) w[i]=read();
for(int i=n-1;i>=0;i--) Sam.add(s[i]-'a',w[i]);
//倒着建Sam
Sam.solve();
}