题目链接
一年一度的“幻影阁夏日品酒大会”隆重开幕了。
大会包含品尝和趣味挑战两个环节,分别向优胜者颁发“首席品酒家”和“首席猎手”两个奖项,吸引了众多品酒师参 加。
在大会的晩餐上,调酒师 Rainbow 调制了 n 杯鸡尾酒。
这 n 杯鸡尾酒排成一行,其中第 i 杯酒 (1≤i≤n) 被贴上了一个标签 si ,每个标签都是 26 个小写英文字母之 -。
设 str(l,r) 表示第 l 杯酒到第 r 杯酒的 r−l+1 个标签顺次连接构成的字符串。
若 str(p,p0)=str(q,q0) ,其中 1≤p≤p0≤n,1≤q≤q0≤n,p≠q,p0−p+1=q0−q+1=r ,则称第 p 杯酒与第 q 杯酒是 “ r 相似” 的。
特别地,对于任意的 1≤p,q≤n,p≠q ,第 p 杯酒和第 q 杯酒都是“ 0 相似”的。
在品尝环节上,品酒师 Freda 轻松地评定了每一杯酒的美味度,凭借其专业的水准和经验成功夺取了“首席品酒家” 的称号,其中第 i 杯酒 (1≤i≤n) 的美味度为 ai 。
现在 Rainbow 公布了挑战环节的问题: 本次大会调制的鸡尾酒有一个特点,如果把第 p 杯酒与第 q 杯酒调兑在一 起,将得到一杯美味度为 ap×aq 的酒。
现在请各位品酒师分别对于 r=0,1,2,⋯,n−1 ,统计出有多少种方法可以选出 2 杯 “ r 相似”的酒,并回答选 择 2 杯“ r 相似”的酒调兄可以得到的美味度的最大值。
输入格式
第 1 行包含 1 个正整数 n ,表示鸡尾酒的杯数。
第 2 行包含一个长度为 n 的字符串 S ,其中第 i 个字符表示第 i 杯酒的标签。
第 3 行包含 n 个整数,相邻整数之间用单个空格隔开,其中第 i 个整数表示第 i 杯酒的美味度 ai 。
输出格式
输出共包括 n 行。
第 i 行输出 2 个整数,中间用单个空格隔开。
第 1 个整数表示选出两杯“ (i−1) 相似”的酒的方案数,第 2 个整数表示选出两杯 “ (i−1) 相似”的酒调兑可以得 到的最大美味度。
若不存在两杯 “ (i−1) 相似” 的酒, 这两个数均为 0 。
数据范围

解题思路
后缀数组
考虑后缀数组中 height[i] 的定义:所有后缀中排名为 i 的后缀和排名为 i−1 的后缀的最长公共前缀长度,其中长度为子串的实际长度,不考虑填补的字符。而本题要求任意两个相等子串长度为 i∈[0,n−1] 的方案数,即对于要求的长度 i,找出某一个长度为 i 的子串的数量 cnt,其
贡献为 C2cnt,累加所有这样的子串贡献即可
现在关键在于如何将其转化为 height 数组?
由于所有的子串都是所有后缀的前缀,可以考虑按长度从大到小计算,从相同长度为 n−1 开始,此时仅有两个长度为 n−1 的子串,如果存在相等的话,这两个排名一定是相邻的,因为两个子串除了最后一个字符完全相等,这时可用并查集维护相同长度的信息,即相同长度的个数,同时计算答案,相同长度递减为 i 时,同理,只需要合并那些相邻相同长度为 i 的后缀,而此时不会存在某两个子串相同长度为 i 却没有统计到的情况,为什么?考虑任意一个子串对应的后缀所在的排名,假设其比另外一个子串对应的后缀的排名要靠前,而其两后缀的前缀长度最长,即最靠近,即相邻,而这时将这些后缀合并正好统计到这些数量,而相同长的子串会对相同长度短的子串有影响,所以需要按长度从大到小计算,因为每次合并都是合并相邻排名的后缀,当两后缀集合不是同一个集合时,由于是按长度从大到小计算,所以两集合后缀的最长前缀不会小于当前相同长度,合并的同时更新答案,另外还需要计算任意两个子串的权值最大乘积,按正负性,无非就三种情况:正×正,负×负,正×负,即统计 最大值,次大值,最小值,次小值,最后答案为 max(最大值×次大值,最小值×次小值),同样可以用并查集维护这些信息
代码
#include <bits/stdc++.h>
#define help {cin.tie(NULL); cout.tie(NULL);}
#define pb push_back
#define fi first
#define se second
#define mkp make_pair
using namespace std;
typedef long long LL;
typedef pair<int, int> PII;
typedef pair<LL, LL> PLL;
template <typename T> bool chkMax(T &x, T y) { return (y > x) ? x = y, 1 : 0; }
template <typename T> bool chkMin(T &x, T y) { return (y < x) ? x = y, 1 : 0; }
template <typename T> void inline read(T &x) {
int f = 1; x = 0; char s = getchar();
while (s < '0' || s > '9') { if (s == '-') f = -1; s = getchar(); }
while (s <= '9' && s >= '0') x = x * 10 + (s ^ 48), s = getchar();
x *= f;
}
const int N=3e5+5,inf=0x3f3f3f3f;
int n,m,a[N],sa[N],height[N],rk[N],c[N],x[N],y[N];
char s[N];
PLL res[N];
vector<int> b[N];
int fa[N],sz[N];
LL max1[N],max2[N],min1[N],min2[N];
void get_sa()
{
for(int i=1;i<=n;i++)c[x[i]=s[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 cnt=0;
for(int i=n-k+1;i<=n;i++)y[++cnt]=i;
for(int i=1;i<=n;i++)
if(sa[i]>k)y[++cnt]=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]]=1,cnt=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])?cnt:++cnt;
if(cnt==n)break;
m=cnt;
}
}
void get_height()
{
for(int i=1;i<=n;i++)rk[sa[i]]=i;
for(int i=1,k=0;i<=n;i++)
{
if(rk[i]==1)continue;
if(k)k--;
int j=sa[rk[i]-1];
while(i+k<=n&&s[i+k]==s[j+k])k++;
height[rk[i]]=k;
}
}
LL C(int x)
{
return x*(x-1ll)/2;
}
int find(int x)
{
return x==fa[x]?x:fa[x]=find(fa[x]);
}
PLL cal(int r)
{
static LL cnt=0,mx=LONG_LONG_MIN;
for(int x:b[r])
{
int a=find(x),b=find(x-1);
if(a==b)continue;
cnt-=C(sz[a]);
cnt-=C(sz[b]);
fa[a]=b;
sz[b]+=sz[a];
cnt+=C(sz[b]);
if(max1[b]<max1[a])
{
max2[b]=max(max1[b],max2[a]);
max1[b]=max1[a];
}
else
max2[b]=max(max2[b],max1[a]);
if(min1[b]>min1[a])
{
min2[b]=min(min1[b],min2[a]);
min1[b]=min1[a];
}
else
min2[b]=min(min2[b],min1[a]);
mx=max({mx,max1[b]*max2[b],min1[b]*min2[b]});
}
if(cnt==0)return {0,0};
return {cnt,mx};
}
int main()
{
scanf("%d",&n);
scanf("%s",s+1);
for(int i=1;i<=n;i++)scanf("%d",&a[i]);
m='z';
get_sa();
get_height();
for(int i=1;i<=n;i++)
{
fa[i]=i,sz[i]=1;
min1[i]=max1[i]=a[sa[i]];
min2[i]=inf,max2[i]=-inf;
}
for(int i=2;i<=n;i++)b[height[i]].pb(i);
for(int i=n-1;i>=0;i--)res[i]=cal(i);
for(int i=0;i<n;i++)printf("%lld %lld\n",res[i].fi,res[i].se);
return 0;
}
__EOF__
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!