luogu P2178 [NOI2015] 品酒大会
题面传送门
用SA做起来是很妙的一道题可惜我不会
智商不够可以拿自动机来凑。
反向建立SAM,则每两个后缀的lcp就是lca。
对于每个节点用子树合并。注意有负数所以要存最大和最小值。
然后后缀和一遍即可。
时间复杂度\(O(n)\)
code:
#include<cstdio>
#include<algorithm>
#include<cstring>
#define I inline
#define ll long long
#define N 600039
#define max(a,b) ((a)>(b)?(a):(b))
using namespace std;
int n,x,a[N];ll ans[N],tot[N],siz[N],w[9];
char b[N];
struct ques1{
int a,b;
I void make(){a=b=2e9;}
ques1 operator +(const ques1 &s)const{w[1]=a;w[2]=b;w[3]=s.a;w[4]=s.b;sort(w+1,w+5);return (ques1){w[1],w[2]};}
}f1[N];
struct ques2{
int a,b;
I void make(){a=b=-2e9;}
ques2 operator +(const ques2 &s)const{w[1]=a;w[2]=b;w[3]=s.a;w[4]=s.b;sort(w+1,w+5);return (ques2){w[4],w[3]};}
}f2[N];
struct SAM{
int son[N][26],fa[N],len[N],cnt=1,last=1,now,pus,cur,p;
I void insert(int x){
p=last;now=last=++cnt;len[now]=len[p]+1;siz[now]=1;
while(p&&!son[p][x]) son[p][x]=now,p=fa[p];
if(!p) return (void)(fa[now]=1);
cur=son[p][x];if(len[cur]==len[p]+1) fa[now]=cur;
else {
fa[pus=++cnt]=fa[cur];memcpy(son[pus],son[cur],sizeof(son[cur]));len[pus]=len[p]+1;fa[now]=fa[cur]=pus;
while(p&&son[p][x]==cur) son[p][x]=pus,p=fa[p];
}
}
}g;
struct yyy{int to,z;};
struct ljb{
int head,h[N];yyy f[N];
I void add(int x,int y){f[++head]=(yyy){y,h[x]};h[x]=head;}
}s;
I void dfs(int x){
int id=g.len[x],cur;yyy tmp;
for(cur=s.h[x];cur;cur=tmp.z) tmp=s.f[cur],dfs(tmp.to),ans[id]+=siz[x]*siz[tmp.to],siz[x]+=siz[tmp.to],f1[x]=f1[x]+f1[tmp.to],f2[x]=f2[x]+f2[tmp.to];
if(siz[x]>=2)tot[id]=max((ll)f1[x].a*f1[x].b,max((ll)f2[x].a*f2[x].b,tot[id]));
}
int main(){
freopen("1.in","r",stdin);
register int i;scanf("%d%s",&n,b+1);for(i=1;i<=2*n;i++) f1[i].make(),f2[i].make();for(i=1;i<=n;i++) scanf("%d",&a[i]);
for(i=n;i;i--) g.insert(b[i]-'a'),tot[i]=-2e18,f1[g.last].a=f2[g.last].a=a[i];
for(i=2;i<=g.cnt;i++)s.add(g.fa[i],i);dfs(1);for(i=n-1;~i;i--) ans[i]+=ans[i+1],tot[i]=max(tot[i],tot[i+1]);
for(i=0;i<n;i++)printf("%lld %lld\n",ans[i],(tot[i]==-2e18)?0:tot[i]);
}