[后缀数组][并查集] Luogu P2178 品酒大会
题解
- 我们可以考虑从大到小枚举height,然后按顺序每一次将其两边的后缀集合合并,用并查集实现
- 这样我们每一次合并的后缀集合的组合一定满足它们的lcp会大于等于height[i],这两个集合的合并对任意r<= h[i]的答案都有其大小乘积的贡献
- 最大值同样维护即可,注意由于可能有负数,所以可以再维护一个最小值
代码
1 #include <cstdio> 2 #include <iostream> 3 #include <algorithm> 4 #define ll long long 5 using namespace std; 6 const ll N=300010,inf=5e18; 7 ll n,t[N],t2[N],sa[N],rank[N],height[N],c[N],a[N],fa[N],cnt[N],mx[N],Mx[N],Mn[N],size[N]; 8 char s[N]; 9 bool cmp(ll x,ll y) { return height[x]>height[y]; } 10 ll find(ll x) { return fa[x]==x?x:fa[x]=find(fa[x]); } 11 void get_SA() 12 { 13 ll l='z',*x=t,*y=t2; 14 for (ll i=0;i<=l;i++) c[i]=0; 15 for (ll i=1;i<=n;i++) c[x[i]=s[i]]++; 16 for (ll i=1;i<=l;i++) c[i]+=c[i-1]; 17 for (ll i=n;i>=1;i--) sa[c[x[i]]--]=i; 18 for (ll k=1;k<n;k<<=1) 19 { 20 ll p=0; 21 for (ll i=n-k+1;i<=n;i++) y[++p]=i; 22 for (ll i=1;i<=n;i++)if(sa[i]>k) y[++p]=sa[i]-k; 23 for (ll i=0;i<=l;i++) c[i]=0; 24 for (ll i=1;i<=n;i++) c[x[y[i]]]++; 25 for (ll i=1;i<=l;i++) c[i]+=c[i-1]; 26 for (ll i=n;i>=1;i--) sa[c[x[y[i]]]--]=y[i]; 27 swap(x,y),p=x[sa[1]]=1; 28 for (ll i=2;i<=n;i++) x[sa[i]]=y[sa[i]]==y[sa[i-1]]&&y[sa[i]+k]==y[sa[i-1]+k]?p:++p; 29 if (p>=n) break; 30 l=p; 31 } 32 for (ll i=1;i<=n;i++) rank[sa[i]]=i; 33 ll k=0; 34 for (ll i=1;i<=n;i++) 35 { 36 ll j=sa[rank[i]+1]; 37 if (!j) continue; 38 if (k) k--; 39 while (s[i+k]==s[j+k]) k++; 40 height[rank[i]]=k; 41 } 42 for (ll i=1;i<=n;i++) a[i]=i; 43 sort(a+1,a+n,cmp); 44 } 45 int main() 46 { 47 scanf("%lld",&n); for (ll i=0;i<=n;i++) fa[i]=i; 48 scanf("%s",s+1),get_SA(); 49 for (ll i=1;i<=n;i++) scanf("%lld",&Mx[rank[i]]),size[rank[i]]=1,mx[i-1]=-inf,Mn[rank[i]]=Mx[rank[i]]; 50 for (ll i=1;i<n;i++) 51 { 52 ll x=find(a[i]),y=find(a[i]+1); 53 cnt[height[a[i]]]+=size[x]*size[y],mx[height[a[i]]]=max(mx[height[a[i]]],max(Mn[x]*Mn[y],Mx[x]*Mx[y])),Mn[x]=min(Mn[x],Mn[y]),Mx[x]=max(Mx[x],Mx[y]),size[x]+=size[y],fa[y]=x; 54 } 55 for (ll i=n-2;i>=0;i--) cnt[i]+=cnt[i+1],mx[i]=max(mx[i],mx[i+1]); 56 for (ll i=0;i<n;i++) printf("%lld %lld\n",cnt[i],cnt[i]?mx[i]:0); 57 }