传送门
SOL
给定一个母串
S
S
S
题意:对于长度为
l
e
n
len
len的子串集合
a
i
a_{i}
ai,
求
a
i
a_{i}
ai中相同字串(不同位置)之间的组合方案之和,和
a
i
a_{i}
ai 中 任意两字串权值乘积的最大值
一子串权值为该子串最左端字符对应的权值(可能为负数)。
求出每一子串出现总数
c
n
t
cnt
cnt,
d
[
i
]
d[i]
d[i]表示长度为
i
i
i的情况之和,
d
[
i
]
+
=
c
n
t
∗
(
c
n
t
−
1
)
/
2
d[i]+=cnt*(cnt-1)/2
d[i]+=cnt∗(cnt−1)/2;
反向建出SAM,同一节点里的权值相同,维护最大,次大,最小,次小,上传到父亲,
用线段树区间更新
l
e
n
[
f
a
[
i
]
]
+
1
len[fa[i]]+1
len[fa[i]]+1——
l
e
n
[
i
]
len[i]
len[i]区间的答案
Update 2019.08.07:
好像不用线段树用差分就可以。。。留给读者自己思考吧(这个不难想)
CODE
#include<bits/stdc++.h>
#define pf printf
#define sf scanf
#define re register
using namespace std;
#define cs const
#define ll long long
#define lc (p<<1)
#define rc ((p<<1)|1)
cs int N=6e5+10;
cs ll inf=1e18;
ll dlt[N],cnt[N];
int vl[N],len[N],fa[N],ch[N][30],tot,las,n,d[N],g[N];
ll num[N<<1],mx[N],dmx[N],mn[N],dmn[N];
inline void build(int p,int l,int r){
num[p]=-inf;
if(l==r)return;
int mid=(l+r)>>1;
build(lc,l,mid);
build(rc,mid+1,r);
}
inline void update(int p,int l,int r,int ql,int qr,ll v){
if(ql>qr)return;
if(ql<=l&&r<=qr){
num[p]=max(num[p],v);
return;
}
int mid=(l+r)>>1;;
if(qr<=mid)update(lc,l,mid,ql,qr,v);
else if(ql>mid)update(rc,mid+1,r,ql,qr,v);
else update(lc,l,mid,ql,qr,v),update(rc,mid+1,r,ql,qr,v);
}
struct node{
ll mx;
int id,l,r;
};
inline void bfs(){
queue<node> q;
q.push((node){num[1],1,1,n});
while(!q.empty()){
node t=q.front();q.pop();
if(t.l==t.r){
mx[t.l]=t.mx;
continue;
}
int mid=(t.l+t.r)>>1;
q.push((node){max(t.mx,num[t.id<<1]),t.id<<1,t.l,mid});
q.push((node){max(t.mx,num[(t.id<<1)|1]),(t.id<<1)|1,mid+1,t.r});
}
}
inline void init(){
las=tot=1;
}
inline void add(int c){
int cur=++tot,p=las,q=ch[p][c];
las=cur;len[cur]=len[p]+1;mx[cur]=mn[cur]=vl[len[cur]];cnt[cur]=1;
for(;p&&!q;p=fa[p],q=ch[p][c])ch[p][c]=cur;
if(!p){fa[cur]=1;return;}
if(len[p]+1==len[q]){fa[cur]=q;return;}
int clo=++tot;
fa[clo]=fa[q];fa[q]=fa[cur]=clo;
len[clo]=len[p]+1;
memcpy(ch[clo],ch[q],sizeof ch[clo]);
for(;p&&ch[p][c]==q;p=fa[p])ch[p][c]=clo;
}
inline void get(){
for(int re i=1;i<=tot;++i)++d[len[i]];
for(int re i=1;i<=tot;++i)d[i]+=d[i-1];
for(int re i=1;i<=tot;++i)g[d[len[i]]--]=i;
for(int re i=tot;i>=2;--i){
cnt[fa[g[i]]]+=cnt[g[i]];
ll now=(cnt[g[i]]-1)*cnt[g[i]]/2;
dlt[len[fa[g[i]]]+1]+=now;
dlt[len[g[i]]+1]-=now;
if(mx[fa[g[i]]]>mx[g[i]]){
if(dmx[fa[g[i]]]<mx[g[i]])dmx[fa[g[i]]]=mx[g[i]];
}
else {
dmx[fa[g[i]]]=mx[fa[g[i]]];mx[fa[g[i]]]=mx[g[i]];
if(dmx[g[i]]>dmx[fa[g[i]]])dmx[fa[g[i]]]=dmx[g[i]];
}
if(mn[fa[g[i]]]<mn[g[i]]){
if(dmn[fa[g[i]]]>mn[g[i]])dmn[fa[g[i]]]=mn[g[i]];
}
else {
dmn[fa[g[i]]]=mn[fa[g[i]]];mn[fa[g[i]]]=mn[g[i]];
if(dmn[g[i]]<dmn[fa[g[i]]])dmn[fa[g[i]]]=dmn[g[i]];
}
if(dmx[g[i]]!=-inf)update(1,1,n,len[fa[g[i]]]+1,len[g[i]],max(mx[g[i]]*dmx[g[i]],mn[g[i]]*dmn[g[i]]));
}
bfs();
for(int re i=1;i<=n-1;++i){
dlt[i]+=dlt[i-1];
printf("%lld %lld\n",dlt[i],dlt[i]==0? 0:mx[i]);
}
}
char s[N];
ll lmx=-inf,ldmx=-inf,lmn=inf,ldmn=inf;
signed main (){
init();
sf("%d",&n);
sf("%s",s+1);
for(int re i=1;i<=n*2;++i)mx[i]=dmx[i]=-inf,mn[i]=dmn[i]=inf;
build(1,1,n);
for(int re i=n;i>=1;--i){
sf("%d",&vl[i]);
if(vl[i]>lmx)ldmx=lmx,lmx=vl[i];
else if(vl[i]>ldmx)ldmx=vl[i];
if(vl[i]<lmn)ldmn=lmn,lmn=vl[i];
else if(vl[i]<ldmn)ldmn=vl[i];
}
for(int re i=n;i>=1;--i)add(s[i]-'a');
printf("%lld %lld\n",1ll*n*(n-1)/2,max(lmx*ldmx,ldmn*lmn));
get();
return 0;
}