题意:对于每个 (k) ,求 (S[p,p+k-1]==S[q,q+k-1],(p,q)为无序二元组) 的方案数,并求出在 (k) 一定时,(vl[p] imes vl[q]) 的最大值。
(SA) 并查集:我们把 (ht[]) 当做边,合并时计算两个集合产生的贡献;
好像还可以单调栈+ST表做吧(没写。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define ll long long
#define R register int
using namespace std;
namespace Luitaryi {
inline int g() { R x=0,f=1;
register char s; while(!isdigit(s=getchar())) f=s=='-'?-1:f;
do x=x*10+(s^48); while(isdigit(s=getchar())); return x*f;
} const int N=300010;
int n,a[N],sa[N],rk[N],ht[N],c[N],x[N],y[N];
char s[N];
inline void init() {
R m=122;
for(R i=1;i<=n;++i) ++c[x[i]=s[i]];
for(R i=2;i<=m;++i) c[i]+=c[i-1];
for(R i=n;i;--i) sa[c[x[i]]--]=i;
for(R t=1,top=0;top<n;m=top,t<<=1) {
top=0;
for(R i=n-t+1;i<=n;++i) y[++top]=i;
for(R i=1;i<=n;++i) if(sa[i]>t) y[++top]=sa[i]-t;
memset(c,0,(m+1)<<2);
for(R i=1;i<=n;++i) ++c[x[i]];
for(R i=2;i<=m;++i) c[i]+=c[i-1];
for(R i=n;i;--i) sa[c[x[y[i]]]--]=y[i];
swap(x,y),x[sa[1]]=top=1;
for(R i=2;i<=n;++i)
x[sa[i]]=(y[sa[i]]==y[sa[i-1]] && y[sa[i]+t]==y[sa[i-1]+t])?top:++top;
} for(R i=1;i<=n;++i) rk[sa[i]]=i;
R k=0;
for(R i=1,j;i<=n;++i) {
if(k) --k; j=sa[rk[i]-1];
while(s[i+k]==s[j+k]) ++k;
ht[rk[i]]=k;
}
}
struct node {
int ht,id;
inline bool operator < (const node& that) const
{return ht>that.ht;}
}b[N];
int fa[N],sz[N],mx[N],mn[N]; ll ans[N],anss[N];
inline int getf(int x) {return x==fa[x]?x:fa[x]=getf(fa[x]);}
inline void merge(int u,int v,int t) {
R fu=getf(u),fv=getf(v);
if(sz[fu]>sz[fv]) swap(u,v),swap(fu,fv);
ans[t]+=1ll*sz[fu]*sz[fv];
anss[t]=max(1ll*anss[t],max(1ll*mx[fu]*mx[fv],1ll*mn[fu]*mn[fv]));
mx[fv]=max(mx[fv],mx[fu]);
mn[fv]=min(mn[fv],mn[fu]);
fa[fu]=fv,sz[fv]+=sz[fu];
}
inline void main() {
n=g(),scanf("%s",s+1);
for(R i=1;i<=n;++i) a[i]=g();
init();
for(R i=1;i<=n;++i) b[i].ht=ht[i],b[i].id=i;
sort(b+2,b+n+1);
memset(anss,0xbf,sizeof anss);
for(R i=1;i<=n;++i) fa[i]=i,sz[i]=1,mx[i]=mn[i]=a[i];
for(R i=2;i<=n;++i)
merge(sa[b[i].id-1],sa[b[i].id],b[i].ht);
for(R i=n-1;~i;--i)
ans[i]+=ans[i+1],anss[i]=max(anss[i],anss[i+1]);
for(R i=0;i<n;++i)
if(ans[i]) printf("%lld %lld
",ans[i],anss[i]);
else puts("0 0");
}
} signed main() {Luitaryi::main(); return 0;}
(SAM) 把串倒着插,每个点的贡献即为 (以i为根的路径条数 imes 产生的最值) 转移时类似淀粉质(详见代码)
#include<iostream>
#include<cstdio>
#include<cstring>
#define ll long long
#define R register int
using namespace std;
namespace Luitaryi {
inline int g() { R x=0,f=1;
register char s; while(!isdigit(s=getchar())) f=s=='-'?-1:f;
do x=x*10+(s^48); while(isdigit(s=getchar())); return x*f;
} const int N=600010,Inf=0x3f3f3f3f;
int n,tot=1,lst=1;
int fa[N],c[N][26],len[N],a[N],vl[N],sz[N];
char s[N>>1];
ll ans1[N>>1],ans2[N>>1];
inline void add(int ch) {
R p=lst,np=lst=++tot;
len[np]=len[p]+1; sz[np]=1;
while(p&&!c[p][ch]) c[p][ch]=np,p=fa[p];
if(!p) return fa[np]=1,void();
R q=c[p][ch];
if(len[q]==len[p]+1) return fa[np]=q,void();
R nq=++tot;
memcpy(c[nq],c[q],26<<2);
fa[nq]=fa[q],len[nq]=len[p]+1;
fa[np]=fa[q]=nq;
while(p&&c[p][ch]==q) c[p][ch]=nq,p=fa[p];
}
int cnt,vr[N],nxt[N],fir[N],mx[N],mn[N];
inline void add(int u,int v)
{vr[++cnt]=v,nxt[cnt]=fir[u],fir[u]=cnt;}
inline void dfs(int u) {
if(sz[u]) mx[u]=mn[u]=vl[u];
else mx[u]=-Inf,mn[u]=Inf;
for(R i=fir[u];i;i=nxt[i]) {
R v=vr[i];
dfs(v);
if(mx[u]!=-Inf&&mn[u]!=Inf&&mx[v]!=-Inf&&mn[v]!=Inf)
ans1[len[u]]=max(ans1[len[u]],max(1ll*mx[u]*mx[v],1ll*mn[u]*mn[v]));
mx[u]=max(mx[u],mx[v]);
mn[u]=min(mn[u],mn[v]);
ans2[len[u]]+=1ll*sz[u]*sz[v];
sz[u]+=sz[v];
}
}
inline void main() {
n=g(),scanf("%s",s+1);
for(R i=1;i<=n;++i) a[i]=g();
for(R i=n;i;--i) add(s[i]-'a'),vl[lst]=a[i];
for(R i=2;i<=tot;++i) add(fa[i],i);
memset(ans1,0xcf,sizeof ans1); dfs(1);
for(R i=n-1;~i;--i)
ans1[i]=max(ans1[i],ans1[i+1]),
ans2[i]+=ans2[i+1];
for(R i=0;i<n;++i) if(ans2[i])
printf("%lld %lld
",ans2[i],ans1[i]);
else puts("0 0");
}
} signed main() {Luitaryi::main(); return 0;}
2019.01.09