题意:对于每个 \(k\) ,求 \(S[p,p+k-1]==S[q,q+k-1],(p,q)为无序二元组\) 的方案数,并求出在 \(k\) 一定时,\(vl[p]\times 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\n",ans[i],anss[i]); else puts("0 0"); } } signed main() {Luitaryi::main(); return 0;}
\(SAM\) 把串倒着插,每个点的贡献即为 \(以i为根的路径条数 \times 产生的最值\) 转移时类似淀粉质(详见代码)
#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\n",ans2[i],ans1[i]); else puts("0 0"); } } signed main() {Luitaryi::main(); return 0;}
2019.01.09
来源:https://www.cnblogs.com/Jackpei/p/12177332.html