Problem
BZOJ
题意:
∀i∈[1,n−1]
∀
i
∈
[
1
,
n
−
1
]
,求最长公共前缀的长度为i的后缀有多少对,并求权值乘积最大为多少
Solution
这道题的前置题目应该是AHOI2013 差异
在parent树上,祖先表示了这个节点的后缀,那么两节点的lca,就是最长公共后缀。
那么考虑翻转原串,建出后缀自动机,拓扑排序之后再跑dp,注意由于权值可能为负,所以还要维护一个mn。注意在insert时新建的nq也要初始化mx和mn。
Code
#include <cstring>
#include <cstdio>
#define rg register
using namespace std;
typedef long long ll;
const int maxn=600010;
const ll INF=(ll)2e18;
int n,lst=1,tot=1,val[maxn],pre[maxn],ch[maxn][26],l[maxn],sz[maxn];
int a[maxn],b[maxn];
ll cnt[maxn],mx[maxn],mn[maxn],ans1[maxn],ans2[maxn],out1[maxn],out2[maxn];
char s[maxn];
template <typename Tp> inline void getmin(Tp &x,Tp y){if(y<x) x=y;}
template <typename Tp> inline void getmax(Tp &x,Tp y){if(y>x) x=y;}
template <typename Tp> inline void read(Tp &x)
{
x=0;int f=0;char ch=getchar();
while(ch!='-'&&(ch<'0'||ch>'9')) ch=getchar();
if(ch=='-') f=1,ch=getchar();
while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
if(f) x=-x;
}
void insert(int c,int pos)
{
int np=++tot,p=lst;
lst=np;l[np]=l[p]+1;sz[np]=1;mn[np]=mx[np]=val[pos];
for(;p&&!ch[p][c];p=pre[p]) ch[p][c]=np;
if(!p) pre[np]=1;
else
{
int q=ch[p][c];
if(l[q]==l[p]+1) pre[np]=q;
else
{
int nq=++tot;mx[nq]=-INF;mn[nq]=INF;
l[nq]=l[p]+1;pre[nq]=pre[q];pre[q]=pre[np]=nq;
memmove(ch[nq],ch[q],sizeof(ch[q]));
for(;ch[p][c]==q;p=pre[p]) ch[p][c]=nq;
}
}
}
void top()
{
for(rg int i=1;i<=tot;i++) b[l[i]]++,ans2[i]=-INF;
for(rg int i=1;i<=n;i++) b[i]+=b[i-1];
for(rg int i=1;i<=tot;i++) a[b[l[i]]--]=i;
}
int main()
{
#ifndef ONLINE_JUDGE
freopen("in.txt","r",stdin);
#endif
read(n);scanf("%s",s+1);
for(rg int i=1;i<=n;i++) read(val[i]);
for(rg int i=n;i;i--) insert(s[i]-'a',i),out2[i]=-INF;
top();out2[0]=-INF;mx[1]=-INF;mn[1]=INF;
for(rg int i=tot,p;i;i--)
{
p=a[i];
ans1[pre[p]]+=(ll)sz[pre[p]]*sz[p];sz[pre[p]]+=sz[p];
if(mx[pre[p]]^(-INF)) getmax(ans2[pre[p]],mx[pre[p]]*mx[p]);
if(mn[pre[p]]^INF) getmax(ans2[pre[p]],mn[pre[p]]*mn[p]);
getmax(mx[pre[p]],mx[p]);getmin(mn[pre[p]],mn[p]);
}
for(rg int i=1;i<=tot;i++) out1[l[i]]+=ans1[i],getmax(out2[l[i]],ans2[i]);
for(rg int i=n-2;~i;i--) out1[i]+=out1[i+1],getmax(out2[i],out2[i+1]);
for(rg int i=0;i<n;i++) printf("%lld %lld\n",out1[i],out1[i]?out2[i]:0);
return 0;
}