【洛谷P2178】品酒大会
题目
题目链接:https://www.luogu.com.cn/problem/P2178
一年一度的“幻影阁夏日品酒大会”隆重开幕了。大会包含品尝和趣味挑战 两个环节,分别向优胜者颁发“首席品酒家”和“首席猎手”两个奖项,吸引了众多品酒师参加。
在大会的晚餐上,调酒师 Rainbow 调制了 \(n\) 杯鸡尾酒。这 \(n\) 杯鸡尾酒排成一行,其中第 \(n\) 杯酒 (\(1 ≤ i ≤ n\)) 被贴上了一个标签 \(s_i\) ,每个标签都是 \(26\) 个小写 英文字母之一。设 \(str(l, r)\) 表示第 \(l\) 杯酒到第 \(r\) 杯酒的 \(r - l + 1\) 个标签顺次连接构成的字符串。若 \(str(p, p_0) = str(q, q_0)\),其中 \(1 ≤ p ≤ p_0 ≤ n\), \(1 ≤ q ≤ q_0 ≤ n\), \(p ≠ q\),\(p_0-p+1 = q_0 - q + 1 = r\) ,则称第 \(p\) 杯酒与第 \(q\) 杯酒是“ \(r\) 相似” 的。当然两杯“ \(r\) 相似”(\(r > 1\))的酒同时也是“ \(1\) 相似”、“ \(2\) 相似”、……、“ \((r - 1)\) 相似”的。特别地,对于任意的 \(1 ≤ p ,q ≤ n,p ≠ q\),第 \(p\) 杯酒和第 \(q\) 杯酒都 是“ \(0\) 相似”的。
在品尝环节上,品酒师 Freda 轻松地评定了每一杯酒的美味度,凭借其专业的水准和经验成功夺取了“首席品酒家”的称号,其中第 \(i\) 杯酒 (\(1 ≤ i ≤ n\)) 的 美味度为 \(a_i\) 。现在 Rainbow 公布了挑战环节的问题:本次大会调制的鸡尾酒有一个特点,如果把第 \(p\) 杯酒与第 \(q\) 杯酒调兑在一起,将得到一杯美味度为 \(a_p\times a_q\) 的 酒。现在请各位品酒师分别对于 \(r = 0,1,2,⋯,n-1\) ,统计出有多少种方法可以 选出 \(2\) 杯“ \(r\) 相似”的酒,并回答选择 \(2\) 杯“\(r\) 相似”的酒调兑可以得到的美味度的最大值。
思路
直接上后缀数组求出 height,然后按照 height 从大到小枚举,并查集合并两个块,同时维护好每个块中的最大值,最小值和大小即可。
时间复杂度 \(O(n\log n)\)。
代码
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=300010;
int n,m,a[N],sa[N],rk[N],x[N],y[N],c[N],height[N],father[N],siz[N],maxa[N],mina[N];
ll ans[N][2];
char s[N];
vector<int> pos[N];
void SA()
{
for (int i=1;i<=n;i++) x[i]=s[i],c[x[i]]++;
for (int i=2;i<=m;i++) c[i]+=c[i-1];
for (int i=n;i>=1;i--) sa[c[x[i]]--]=i;
for (int k=1;k<=n;k<<=1)
{
int num=0;
for (int i=n-k+1;i<=n;i++) y[++num]=i;
for (int i=1;i<=n;i++) if (sa[i]>k) y[++num]=sa[i]-k;
for (int i=1;i<=m;i++) c[i]=0;
for (int i=1;i<=n;i++) c[x[i]]++;
for (int i=2;i<=m;i++) c[i]+=c[i-1];
for (int i=n;i>=1;i--) sa[c[x[y[i]]]--]=y[i],y[i]=0;
swap(x,y);
x[sa[1]]=num=1;
for (int i=2;i<=n;i++)
x[sa[i]]=(y[sa[i]]==y[sa[i-1]] && y[sa[i]+k]==y[sa[i-1]+k])?num:++num;
m=num;
if (n==m) break;
}
}
void geth()
{
for (int i=1;i<=n;i++) rk[sa[i]]=i;
for (int i=1,k=0;i<=n;i++)
{
if (k) k--;
int j=sa[rk[i]-1];
while (s[i+k]==s[j+k]) k++;
height[rk[i]]=k;
}
}
int find(int x)
{
return x==father[x]?x:father[x]=find(father[x]);
}
int main()
{
scanf("%d%s",&n,s+1);
for (int i=1;i<=n;i++) scanf("%d",&a[i]);
m='z'+1;
SA(); geth();
for (int i=1;i<=n;i++)
{
pos[height[i]].push_back(i);
father[i]=i; siz[i]=1; maxa[i]=mina[i]=a[sa[i]];
}
ans[n][1]=-5e18;
for (int i=n-1;i>=0;i--)
{
ans[i][0]=ans[i+1][0]; ans[i][1]=ans[i+1][1];
for (int j=0;j<pos[i].size();j++)
{
int x=find(pos[i][j]),y=find(pos[i][j]-1);
if (x!=y)
{
ans[i][0]+=1LL*siz[x]*siz[y];
ans[i][1]=max(ans[i][1],max(1LL*maxa[x]*maxa[y],1LL*mina[x]*mina[y]));
ans[i][1]=max(ans[i][1],max(1LL*mina[x]*maxa[y],1LL*maxa[x]*mina[y]));
if (siz[x]<siz[y])
{
siz[y]+=siz[x]; father[x]=y;
maxa[y]=max(maxa[y],maxa[x]);
mina[y]=min(mina[y],mina[x]);
}
else
{
siz[x]+=siz[y]; father[y]=x;
maxa[x]=max(maxa[x],maxa[y]);
mina[x]=min(mina[x],mina[y]);
}
}
}
}
for (int i=0;i<n;i++)
{
if (ans[i][1]==-5e18) ans[i][1]=0;
printf("%lld %lld\n",ans[i][0],ans[i][1]);
}
return 0;
}