ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

1004. 品酒大会

2022-05-04 17:32:51  阅读:148  来源: 互联网

标签:子串 后缀 大会 leq int 品酒 长度 1004 杯酒


题目链接

1004. 品酒大会

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

输入格式

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

输出格式

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

数据范围

image

解题思路

后缀数组

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

  • 时间复杂度:\(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;
}

标签:子串,后缀,大会,leq,int,品酒,长度,1004,杯酒
来源: https://www.cnblogs.com/zyyun/p/16221673.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有