1004. 品酒大会

题目链接

1004. 品酒大会

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

输入格式

1 行包含 1 个正整数 n ,表示鸡尾酒的杯数。
2 行包含一个长度为 n 的字符串 S ,其中第 i 个字符表示第 i 杯酒的标签。
3 行包含 n 个整数,相邻整数之间用单个空格隔开,其中第 i 个整数表示第 i 杯酒的美味度 ai

输出格式

输出共包括 n 行。
i 行输出 2 个整数,中间用单个空格隔开。
1 个整数表示选出两杯“ (i1) 相似”的酒的方案数,第 2 个整数表示选出两杯 “ (i1) 相似”的酒调兑可以得 到的最大美味度。
若不存在两杯 “ (i1) 相似” 的酒, 这两个数均为 0

数据范围

image

解题思路

后缀数组

考虑后缀数组中 height[i] 的定义:所有后缀中排名为 i 的后缀和排名为 i1 的后缀的最长公共前缀长度,其中长度为子串的实际长度,不考虑填补的字符。而本题要求任意两个相等子串长度为 i[0,n1] 的方案数,即对于要求的长度 i,找出某一个长度为 i 的子串的数量 cnt,其
贡献为 Ccnt2,累加所有这样的子串贡献即可
 height 
由于所有的子串都是所有后缀的前缀,可以考虑按长度从大到小计算,从相同长度为 n1 开始,此时仅有两个长度为 n1 的子串,如果存在相等的话,这两个排名一定是相邻的,因为两个子串除了最后一个字符完全相等,这时可用并查集维护相同长度的信息,即相同长度的个数,同时计算答案,相同长度递减为 i 时,同理,只需要合并那些相邻相同长度为 i 的后缀,而此时不会存在某两个子串相同长度为 i 却没有统计到的情况,考虑任意一个子串对应的后缀所在的排名,假设其比另外一个子串对应的后缀的排名要靠前,而其两后缀的前缀长度最长,即最靠近,即相邻,而这时将这些后缀合并正好统计到这些数量,而相同长的子串会对相同长度短的子串有影响,所以需要按长度从大到小计算,因为每次合并都是合并相邻排名的后缀,当两后缀集合不是同一个集合时,由于是按长度从大到小计算,所以两集合后缀的最长前缀不会小于当前相同长度,合并的同时更新答案,另外还需要计算任意两个子串的权值最大乘积,按正负性,无非就三种情况:×××,即统计 ,最后答案为 max(×,×),同样可以用并查集维护这些信息

  • 时间复杂度:O(nlogn)

代码

// Problem: 品酒大会 // Contest: AcWing // URL: https://www.acwing.com/problem/content/1006/ // Memory Limit: 256 MB // Time Limit: 2000 ms // // Powered by CP Editor (https://cpeditor.org) // %%%Skyqwq #include <bits/stdc++.h> //#define int long long #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__

本文作者acwing_zyy
本文链接https://www.cnblogs.com/zyyun/p/16221673.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   zyy2001  阅读(104)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!
点击右上角即可分享
微信分享提示