[NOI2015]品酒大会

题目描述

一年一度的“幻影阁夏日品酒大会”隆重开幕了。大会包含品尝和趣味挑战 两个环节,分别向优胜者颁发“首席品酒家”和“首席猎手”两个奖项,吸引了众多品酒师参加。 在大会的晚餐上,调酒师 Rainbow 调制了 n 杯鸡尾酒。这 n 杯鸡尾酒排成一行,其中第 n 杯酒 (1 ≤ i ≤ n) 被贴上了一个标签si,每个标签都是 26 个小写 英文字母之一。设 str(l, r)表示第 l 杯酒到第 r 杯酒的 r − l + 1 个标签顺次连接构成的字符串。若 str(p, po) = str(q, qo),其中 1 ≤ p ≤ po ≤ n, 1 ≤ q ≤ qo ≤ n, p ≠ q, po − p + 1 = qo − 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) 的 美味度为 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 。

输入输出样例 输入样例#1:

10 ponoiiipoi 2 1 4 7 4 8 3 6 4 7 输出样例#1: 45 56 10 56 3 32 0 0 0 0 0 0 0 0 0 0 0 0 0 0

输入样例#2:

12 abaabaabaaba 1 -2 3 -4 5 -6 7 -8 9 -10 11 -12

输出样例#2:

66 120 34 120 15 55 12 40 9 27 7 16 5 7 3 -4 2 -4 1 -4 0 0 0 0

恶心的一道题

对于我一个刚学后缀数组的蒟蒻来说这题确实好难啊

经过思考看题解后,终于A辣

首先,这题肯定是后缀数组了

数据范围300000,n^2的暴力肯定会T

我们考虑一个更优的做法

求出height数组后按其中的值排序(然而并不是直接排height,是排的id( 嗯,对,我肯定(不)会这么排的 )

然后用并查集维护每一段的的信息

如果一个height的id和id-1不属于同一个并查集,就将他们合并(height不是表示排名为i和i-1的最长公共前缀嘛)

并查集中维护max,min(两个负数乘起来也是正数),size

对于任意一对r相似,它一定是k(0<k<r)相似的

所以我们在最后统计最终答案时,只需要从大到小循环,将方案数相加,更新min,max值就行辣

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<cmath>
# define int long long
const int M = 300005 ;
const int inf = 1000000000000000000LL ;
using namespace std;
inline int read(){
	char c=getchar(); int x=0,w=1;
	while(c>'9'||c<'0'){if(c=='-') w=-1 ;c=getchar(); }
	while(c>='0'&&c<='9'){x=x*10+c-'0'; c=getchar(); }
	return x*w;
}
int n,m=128;
char s[M];
int a[M],b[M],c[M],sa[M],rank[M],val[M],height[M];
int size[M],f[M];
int tmax[M],tmin[M];
int Ans1[M],Ans2[M];
struct Height{
	int id,height;
	friend inline bool operator < ( Height a, Height b ){
		return a.height > b.height ;
	}
}h[M];
inline void Get_SA(){
    for(int i=1;i<=n;i++){
        a[i]=s[i];
        ++c[a[i]];
    }
    for(int i=2;i<=m;i++)
        c[i]+=c[i-1];
    for(int i=n;i>=1;i--)
        sa[c[a[i]]--]=i;
    for(int k=1;k<=n;k<<=1){
        int num=0;
        for(int i=n-k+1;i<=n;i++)
          b[++num]=i;
        for(int i=1;i<=n;i++)
          if(sa[i]>k)
            b[++num]=sa[i]-k;
        memset(c,0,sizeof(c));
        for(int i=1;i<=n;i++)
          ++c[a[i]];
        for(int i=2;i<=m;i++)
          c[i]+=c[i-1];
        for(int i=n;i>=1;i--){
            sa[c[a[b[i]]]--]=b[i];
            b[i]=0;
        }
        swap(a,b);
        a[sa[1]]=1; num=1;
        for(int i=2;i<=n;i++)
          a[sa[i]]=(b[sa[i]]==b[sa[i-1]]&&b[sa[i]+k]==b[sa[i-1]+k]) ? num : ++num;
        if(num==n) break;
        m=num;  
    }
}
inline void Get_lcp(){
    int k=0;
    for(int i=1;i<=n;i++)
      rank[sa[i]]=i;
    for(int i=1;i<=n;i++){
        if(rank[i]==1) continue ;
        if(k) --k;
        int j=sa[rank[i]-1];
        while(j+k<=n&&i+k<=n&&s[i+k]==s[j+k]) ++k;
        height[rank[i]]=k;
    }
}
int find(int x){
	if(f[x]!=x) f[x]=find(f[x]);
	return f[x];
}
inline void Merge(int x,int y){
	int r1=find(x), r2=find(y);
	int u=height[x];
	Ans1[u]+=size[r1]*size[r2];
	Ans2[u]=max(Ans2[u],max(tmax[r1]*tmax[r2],tmin[r1]*tmin[r2]));
	size[r1]+=size[r2];
	tmax[r1]=max(tmax[r1],tmax[r2]);
	tmin[r1]=min(tmin[r1],tmin[r2]);
	f[r2]=r1;
}
inline void Solve(){
	for(int i=1;i<=n;i++){
		size[i]=1;
		tmax[i]=tmin[i]=val[sa[i]];
	    f[i]=i;
	}
	for(int i=1;i<=n;i++)
	  if(find(h[i].id)!=find(h[i].id-1))
        Merge(h[i].id,h[i].id-1);
}
# undef int
int main(){
# define int long long
	n=read();
	scanf("%s",s+1);
	for(int i=1;i<=n;i++)
	  val[i]=read();
	Get_SA(); 
	Get_lcp();
	for(int i=1;i<=n;i++){
		h[i].id=i;
		h[i].height=height[i];
	    Ans2[i]=-inf;
	}
	sort(h+1,h+n+1);
	Solve();
	for(int i=n-1;i>=0;i--){
		Ans1[i]+=Ans1[i+1];
		Ans2[i]=max(Ans2[i],Ans2[i+1]);
	}
	for(int i=0;i<n;i++)
	  printf("%lld %lld\n",Ans1[i],Ans1[i]==0 ? 0 : Ans2[i]);
	return 0;
}