[bzoj4199][NOI2015]品酒大会

4199: [Noi2015]品酒大会

Time Limit: 10 Sec Memory Limit: 512 MB
Submit: 418 Solved: 231
[Submit][Status][Discuss]
Description

一年一度的“幻影阁夏日品酒大会”隆重开幕了。大会包含品尝和趣味挑战两个环节,分别向优胜者颁发“首席品酒家”和“首席猎手”两个奖项,吸引了众多品酒师参加。

在大会的晚餐上,调酒师 Rainbow 调制了 n 杯鸡尾酒。这 n 杯鸡尾酒排成一行,其中第 i 杯酒 1in 被贴上了一个标签 si ,每个标签都是 26 个小写英文字母之一。设 Str(l,r) 表示第 l 杯酒到第 r 杯酒的 rl+1 个标签顺次连接构成的字符串。若 Str(p,po)=Str(q,qo) ,其中 1ppon 1qqon pq pop+1=qoq+1=r ,则称第 p 杯酒与第 q 杯酒是“ r 相似” 的。当然两杯“r相似” r>1 的酒同时也是“ 1 相似”、“2 相似”、……、“ (r1) 相似”的。特别地,对于任意的 1p,qn pq ,第 p 杯酒和第 q 杯酒都是“ 0 相似”的。

在品尝环节上,品酒师 Freda 轻松地评定了每一杯酒的美味度,凭借其专业的水准和经验成功夺取了“首席品酒家”的称号,其中第 i 杯酒 1in 的美味度为 ai 。现在 Rainbow 公布了挑战环节的问题:本次大会调制的鸡尾酒有一个特点,如果把第 p 杯酒与第 q 杯酒调兑在一起,将得到一杯美味度为 apaq 的酒。现在请各位品酒师分别对于 r=0,1,2,,n1 ,统计出有多少种方法可以选出 2 杯“r相似”的酒,并回答选择 2 杯“r相似”的酒调兑可以得到的美味度的最大值。

Input

输入文件的第 1 行包含 1 个正整数 n ,表示鸡尾酒的杯数。

2 行包含一个长度为 n 的字符串 S,其中第 i 个字符表示第 i 杯酒的标签。

3 行包含 n 个整数,相邻整数之间用单个空格隔开,其中第 i 个整数表示第 i 杯酒的美味度 ai

Output

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

Sample Input

10
ponoiiipoi
2 1 4 7 4 8 3 6 4 7

Sample Output

45 56
10 56
3 32
0 0
0 0
0 0
0 0
0 0
0 0
0 0

HINT

n<=300000

第一问直接建出后缀数组之后,算出每一个点有贡献的区间,用height数组从小到大更新答案。
第二问可以用并查集,因为是从小到大做的,所以产生贡献的区间会不断扩大,这样就可以每次把产生贡献的区间 [l,r] 并起来,并且这一段中的元素最多只会属于两个集合,每次在并查集的过程中保存一下当前集合的最大值和最小值更新答案就行了。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define LL long long
const int N=300010;
char s[N];
LL sum[N],ans[N];
int n,m,t1[N],t2[N],c[N],sa[N],rank[N],height[N],stack[N],l[N],r[N],top,point[N],next[N],tot,en[N],b[N],fa[N],maxn[N],minn[N];
inline int in(){
    int x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){
        if(ch=='-') f=-1;
        ch=getchar();
    }
    while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
    return x*f;
}
inline void add(int x,int y){
    next[++tot]=point[x];point[x]=tot;en[tot]=y;
}
inline bool cmp(int *y,int p,int q,int k){
    int o0,o1;
    o0=p+k>=n?-1:y[p+k];
    o1=q+k>=n?-1:y[q+k];
    return o0==o1&&y[p]==y[q];
}
inline void build_sa(){
    int i,k,p,*x=t1,*y=t2;
    for(m=26,i=0;i<m;++i) c[i]=0;
    for(i=0;i<n;++i) ++c[x[i]=s[i]-'a'];
    for(i=1;i<m;++i) c[i]+=c[i-1];
    for(i=n-1;~i;--i) sa[--c[x[i]]]=i;
    for(k=1;k<=n;k<<=1){
        for(p=0,i=n-k;i<n;++i) y[p++]=i;
        for(i=0;i<n;++i) if(sa[i]>=k) y[p++]=sa[i]-k;
        for(i=0;i<m;++i) c[i]=0;
        for(i=0;i<n;++i) ++c[x[y[i]]];
        for(i=1;i<m;++i) c[i]+=c[i-1];
        for(i=n-1;~i;--i) sa[--c[x[y[i]]]]=y[i];
        swap(x,y);
        x[sa[0]]=0;m=1;
        for(i=1;i<n;++i) x[sa[i]]=cmp(y,sa[i],sa[i-1],k)?m-1:m++;
        if(m>=n) break;
    }
}
inline void build_height(){
    int i,j,k=0;
    for(i=0;i<n;++i) rank[sa[i]]=i;
    for(i=0;i<n;++i){
        if(!rank[i]) continue;
        k=k?k-1:k;
        j=sa[rank[i]-1];
        while(s[i+k]==s[j+k]) ++k;
        height[rank[i]]=k;
    }
}
inline int find(int x){
    if(x!=fa[x]) fa[x]=find(fa[x]);
    return fa[x];
}
int main(){
    int i,j;
    n=in();
    for(i=0;i<n;++i){
        char ch=getchar();
        while(ch<'a'||ch>'z') ch=getchar();
        s[i]=ch;
    }
    build_sa();
    build_height();
    for(i=0;i<n;++i){
        fa[i]=i;
        b[i]=minn[rank[i]]=maxn[rank[i]]=in();
    }
    stack[top=1]=0;
    for(l[0]=0,i=1;i<n;++i){
        while(top&&height[stack[top]]>height[i]) --top;
        l[i]=top?stack[top]+1:0;
        stack[++top]=i;
    }
    stack[top=1]=n-1;
    for(r[n-1]=n-1,i=n-2;i>=0;--i){
        while(top&&height[stack[top]]>=height[i]) --top;
        r[i]=top?stack[top]-1:n-1;
        stack[++top]=i;
    }
    for(i=0;i<n;++i) add(height[i],i);
    memset(ans,128,sizeof(ans));
    for(i=n-1;i;--i){
        sum[i]=sum[i+1];
        ans[i]=ans[i+1];
        for(j=point[i];j;j=next[j]){
            int o=en[j],L=l[o],R=r[o];
            sum[i]+=(LL)(o-L+1)*(LL)(R-o+1);
            int r1=find(L-1),r2=find(R);
            ans[i]=max(ans[i],max((LL)maxn[r1]*(LL)maxn[r2],(LL)minn[r1]*(LL)minn[r2]));
            fa[r2]=r1;
            maxn[r1]=max(maxn[r1],maxn[r2]);
            minn[r1]=min(minn[r1],minn[r2]);
        }
    }
    sort(b,b+n);
    ans[i]=max((LL)b[0]*(LL)b[1],(LL)b[n-1]*(LL)b[n-2]);
    sum[0]=(LL)n*(LL)(n-1)/2;
    for(i=0;i<n;++i) ans[i]=sum[i]?ans[i]:0;
    for(i=0;i<n;++i) printf("%lld %lld\n",sum[i],ans[i]);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值