[后缀数组][并查集] 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
",cnt[i],cnt[i]?mx[i]:0);
57 }
原文地址:https://www.cnblogs.com/Comfortable/p/11321850.html