【BZOJ4199】【NOI2015】—品酒大会(后缀数组)
很显然相似就是所有的后缀
排序后不断加入,用个并查集维护一下合并两段的信息,大小,最大值,最小值就可以了
据说某些题解过不了样例?
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define re register
inline int read(){
char ch=getchar();
int res=0,f=1;
while(!isdigit(ch)){if(ch=='-')f=-f;ch=getchar();}
while(isdigit(ch))res=(res+(res<<2)<<1)+(ch^48),ch=getchar();
return res*f;
}
const int N=300005;
int n,fa[N],mx[N],mn[N],siz[N],pos[N];
ll ans[N],ans1[N],ans2[N];
char s[N];
struct Sa{
int m,a[N],rk[N],sa[N],sa2[N],cnt[N],ht[N];
inline void bucket_sort(){
for(int i=1;i<=m;i++)cnt[i]=0;
for(int i=1;i<=n;i++)++cnt[rk[sa2[i]]];
for(int i=1;i<=m;i++)cnt[i]+=cnt[i-1];
for(int i=n;i>=1;i--)sa[cnt[rk[sa2[i]]]--]=sa2[i];
}
inline void build(){
m=30;
for(int i=1;i<=n;i++)rk[i]=a[i],sa2[i]=i;
bucket_sort();
for(int i=1,pos=0;i<=n&&pos<n;i<<=1){
pos=0;
for(int j=n-i+1;j<=n;j++)sa2[++pos]=j;
for(int j=1;j<=n;j++)if(sa[j]>i)sa2[++pos]=sa[j]-i;
bucket_sort();
swap(rk,sa2);
rk[sa[1]]=1,pos=1;
for(int j=2;j<=n;j++)
rk[sa[j]]=((sa2[sa[j]]==sa2[sa[j-1]])&&(sa2[sa[j]+i]==sa2[sa[j-1]+i])?pos:++pos);
m=pos;
}
for(int i=1;i<=n;i++)rk[sa[i]]=i;
for(int i=1,k=0,j;i<=n;ht[rk[i++]]=k){
for(k?k--:0,j=sa[rk[i]-1];a[i+k]==a[j+k];k++);
}
}
}T;
inline bool comp(int a,int b){
return T.ht[a]>T.ht[b];
}
inline int find(int x){
return fa[x]==x?x:fa[x]=find(fa[x]);
}
inline void chemx(ll &a,ll b){
a=a>b?a:b;
}
inline void merge(int f1,int f2,int k){
f1=find(f1),f2=find(f2);
fa[f2]=f1;
ans1[k]+=1ll*siz[f1]*siz[f2];
siz[f1]+=siz[f2];
chemx(ans[f1],ans[f2]);
chemx(ans[f1],1ll*mx[f1]*mx[f2]);
chemx(ans[f1],1ll*mn[f1]*mn[f2]);
mn[f1]=min(mn[f1],mn[f2]);
mx[f1]=max(mx[f1],mx[f2]);
ans2[k]=max(ans2[k],ans[f1]);
}
int main(){
n=read();
scanf("%s",s+1);
for(int i=1;i<=n;i++)T.a[i]=s[i]-'a'+1;T.build();
for(int i=1;i<=n;i++)siz[i]=1,fa[i]=i,mx[i]=mn[i]=read(),pos[i]=i;
memset(ans2,128,sizeof(ans2));
memset(ans,128,sizeof(ans));
sort(pos+2,pos+n+1,comp);
for(int i=2;i<=n;i++)
merge(T.sa[pos[i]],T.sa[pos[i]-1],T.ht[pos[i]]);
for(int i=n;~i;i--)ans1[i]+=ans1[i+1],ans2[i]=max(ans2[i],ans2[i+1]);
for(int i=0;i<n;i++)cout<<ans1[i]<<" "<<(ans1[i]?ans2[i]:0)<<'\n';
}