【题解】[NOI2015] 品酒大会
\(\text{Solution:}\)
思考一下,两杯酒相似的本质是什么。
考虑把每一杯酒都看成是一个原串的 后缀 ,这样它们的相似程度本质就是其后缀的最长公共前缀。
那这个东西长得就很后缀树 \(dp\) 了。和差异那题一样。(我卡在这里是因为自己傻逼以为每个点都需要维护一个长度复杂的就变平方了实际上只需要求总体的)
看看我们本质求的是什么:有多少对后缀,使得其 \(LCP\ge i.\) 考虑转化成等号,剩下的直接求后缀和即可。
那等号怎么求?有:
\[
f[i]=\sum_{v\in son[i]} siz[v]\times Nowsiz[x],siz[x]=\sum_{v\in son[x]} siz[v]
\]
注意这里的 Nowsiz 数组是动态更新的,否则就算重了。
好 第一问说完了,第二问怎么整?
你最大值无非牵扯到一棵子树内的最大最小值、次大次小值。直接全部维护出来两两相乘取最大值。注意特判一下叶子个数恰好为 \(2\) 的情况,这时会出现点自乘的情况。
剩下也就没什么了,注意后缀树是原串的反串的 parent 树即可。
都想出一大半了结果傻逼要维护所有信息……真是nt)
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N=1e6+10;
typedef long long ll;
inline int Min(int x,int y){return x<y?x:y;}
inline int Max(int x,int y){return x>y?x:y;}
inline int Abs(int x){if(x<0)x=-x;return x;}
namespace SAM{
int len[N],pa[N],siz[N],sum[N],ch[N][26],last=1,tot=1;
vector<int>G[N];
int mx[N],mi[N],mxx[N],mii[N],v[N],mxAns[N];
void insert(const int &c,const int &val){
int p=last;
int np=++tot;
last=tot;len[np]=len[p]+1;
siz[np]=1;v[np]=val;
for(;p&&!ch[p][c];p=pa[p])ch[p][c]=np;
if(!p)pa[np]=1;
else{
int q=ch[p][c];
if(len[q]==len[p]+1)pa[np]=q;
else{
int nq=++tot;
len[nq]=len[p]+1;
pa[nq]=pa[q];pa[q]=pa[np]=nq;
memcpy(ch[nq],ch[q],sizeof ch[q]);
for(;p&&ch[p][c]==q;p=pa[p])ch[p][c]=nq;
}
}
}
inline void ckmax1(int x,int vv){
if(vv>mx[x])mxx[x]=mx[x],mx[x]=vv;
else if(vv>mxx[x])mxx[x]=vv;
}
inline void ckmin1(int x,int vv){
if(vv<mi[x])mii[x]=mi[x],mi[x]=vv;
else if(vv<mii[x])mii[x]=vv;
}
inline void ckmin2(int x,int vv){if(vv<mii[x])mii[x]=vv;}
inline void ckmax2(int x,int vv){if(vv>mxx[x])mxx[x]=vv;}
void dfs(int x){
if(siz[x]){
mxx[x]=-(1LL<<60);
mii[x]=(1LL<<60);
mx[x]=mi[x]=v[x];
}
else{
mx[x]=mxx[x]=-(1LL<<60);
mi[x]=mii[x]=-mx[x];
}
int Sum=0;
for(auto v:G[x]){
dfs(v);
Sum+=siz[x]*siz[v];
siz[x]+=siz[v];
}
if(siz[x]<2)return;
sum[len[x]]+=Sum;
for(auto v:G[x]){
ckmax1(x,mx[v]);ckmax2(x,mxx[v]);
ckmin1(x,mi[v]);ckmin2(x,mii[v]);
}
// printf("(%lld %lld) (%lld %lld)\n",mx[x],mi[x],mxx[x],mii[x]);
}
void Build(){
for(int i=2;i<=tot;++i)G[pa[i]].push_back(i);
dfs(1);
}
}
using namespace SAM;
int n,a[N];
char s[N];
inline bool check(int x,int y){
int Absx=Abs(x);
int Absy=Abs(y);
return Absx<=1000000000&&Absy<=1000000000;
}
void File(){
freopen("P2178_2.in","r",stdin);
freopen("My.out","w",stdout);
}
void work(){
Build();
for(int i=n-1;i>=1;--i)sum[i]+=sum[i+1];
for(int i=1;i<=tot;++i)mxAns[len[i]]=-(1LL<<60);
for(int i=1;i<=tot;++i){
if(siz[i]<2)continue;
if(siz[i]==2){
mxAns[len[i]]=Max(mxAns[len[i]],mx[i]*mxx[i]);
continue;
}
int A[6]={-(1LL<<60),-(1LL<<60),-(1LL<<60),-(1LL<<60),-(1LL<<60),-(1LL<<60)};
if(check(mx[i],mxx[i]))A[0]=mx[i]*mxx[i];
if(check(mx[i],mi[i]))A[1]=mx[i]*mi[i];
if(check(mx[i],mii[i]))A[2]=mx[i]*mii[i];
if(check(mxx[i],mi[i]))A[3]=mxx[i]*mi[i];
if(check(mxx[i],mii[i]))A[4]=mxx[i]*mii[i];
if(check(mii[i],mi[i]))A[5]=mii[i]*mi[i];
sort(A,A+6);
mxAns[len[i]]=Max(mxAns[len[i]],A[5]);
}
for(int i=n-1;~i;--i)mxAns[i]=Max(mxAns[i],mxAns[i+1]);
sum[0]=n*(n-1)/2;
for(int i=0;i<n;++i)printf("%lld %lld\n",sum[i],mxAns[i]==-(1LL<<60)?0:mxAns[i]);
}
signed main(){
File();
scanf("%lld",&n);
scanf("%s",s+1);
for(int i=1;i<=n;++i)scanf("%lld",&a[i]);
reverse(s+1,s+n+1);
reverse(a+1,a+n+1);
for(int i=1;i<=n;++i)insert(s[i]-'a',a[i]);
work();
return 0;
}
/*
8 -9
Max=8 cMax=-9
Min=-9 CMin=8
*/