洛谷P2178 [NOI2015]品酒大会

题目描述

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

在大会的晚餐上,调酒师 Rainbow 调制了 n 杯鸡尾酒。这 n 杯鸡尾酒排成一行,其中第 n 杯酒 (1 ≤ i ≤ n) 被贴上了一个标签 si ,每个标签都是26 个小写 英文字母之一。设 str(l,r) 表示第 l 杯酒到第 r 杯酒的 r - l + 1个标签顺次连接构成的字符串。若 str(p,p0)=str(q,q0),其中 1pp0n, 1qq0n, pq,p0p+1=q0q+1=r ,则称第 p 杯酒与第 q 杯酒是“ r 相似” 的。当然两杯“ r 相似”(r>1)的酒同时也是“ 1 相似”、“ 2 相似”、……、“ (r1) 相似”的。特别地,对于任意的 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 。

说明/提示

【样例说明 1】

用二元组 (p,q) 表示第 p 杯酒与第 q 杯酒。

0 相似:所有 45 对二元组都是 0 相似的,美味度最大的是 8×7=56。

1 相似:(1,8)(2,4)(2,9)(4,9)(5,6)(5,7)(5,10)(6,7)(6,10)(7,10),最大的 8×7=56 。

2 相似:(1,8)(4,9)(5,6) ,最大的4×8=32。

没有 3,4,5,,9 相似的两杯酒,故均输出0 。

【时限1s,内存512M】

跳过其他同专题练习题的原因是,在写完P4248差异以后翻题解区看后缀树的做法,偶然看到有这么一句“品酒大会也可以用这个方法【单调栈】做”

于是我直奔品酒大会,读题五分钟…

恕鄙人才疏学浅,不懂单调栈单调队列,竟然愣是没看出来【事后发现好像真的有这种写法】

手推了一遍样例,感觉就是对于height数组的数值分组进行一堆统计,大数值可以为小数值的分组产生贡献。于是在草稿纸上按着height数组从大到小过了一遍,发现就是这样。但是同数值之间可以产生更复杂的联系,不一定在height定义的排名i和i-1这种范围内。只要前缀相同,排名为i的数组和i-2的数组可能也会产生贡献。

同时height为2的后缀也可能和height为1的后缀对1相似的答案产生贡献…这样又要考虑分组之间的关联。很容易想到倒着先处理height更大的后缀。

但是对于具体怎么处理还是有点头大。看了看刚刚按排名列出来的数组,花了一下产生贡献的分组,发现很显然能产生贡献的一大堆后缀都有相同的起始字母。从这里开始想到倒着处理所有操作,用并查集来维护同一个大组的信息。对于产生贡献的方案数,可以记录一个size数组,按秩合并。而对于最大值贡献,可以记录一下当前集合的最大a值即最大元素,以及当前集合内部元素已经产生的最大贡献。因为先处理的“x相似”的答案对更小的“y相似”的答案都可以产生贡献,所以在合并的时候直接全局变量维护方案数和最大值。

然后死在第二个样例。

仔细一看样例发现只记录集合最大元素显然会死,因为还有负数存在…负负得正,那就再记录一个最小值,一起维护。

然后疯狂通不过样例2,仔细一查居然打错了SA板子…把y[sa[i]]==y[sa[i-1]]写成了y[sa[i]]==y[sa[i]-1]【居然还能过样例1!!】过了样例满心欢喜地提交,被70分砸一脸。然后发现,初始化最小值要到long long的边界…这题数据大到吓死人。

inf设置成1e18,折腾一晚上终于AC。从开始想题到最后写完中间大概经历了三四个小时…期间还因为写炸并查集偷窥题解。怎么说呢,NOI的题果然不好做_(:з」∠)_

偷偷感谢题解区大佬。

#include<iostream>
#include<cstdio>
#include<vector>
using namespace std;
const long long maxn=300010;
const long long inf=1e18;
long long n,m,a[maxn];
long long ans[maxn],max1[maxn],ans1,ans2=-inf,number[maxn];
char s[maxn];
long long c[maxn],x[maxn],y[maxn],sa[maxn],fa[maxn],height[maxn],siz[maxn],maxx[maxn],minn[maxn];
vector<long long>h[maxn];
long long get(long long x){
    if(fa[x]==x)return x;
    else return fa[x]=get(fa[x]);
}
void getHEIGHT(){
    long long k=0;
    for(long long i=1;i<=n;i++){
        if(x[i]==1){
            k=0;
            height[1]=0;
            continue;
        }
        if(k)k--;
        long long j=sa[x[i]-1];
        while(i+k<=n&&j+k<=n&&s[i+k]==s[j+k])k++;
        height[x[i]]=k;
    }
}
void getSA(){
    for(long long i=1;i<=n;i++)c[s[i]]++,x[i]=s[i];
    for(long long i=2;i<=m;i++)c[i]+=c[i-1];
    for(long long i=n;i>=1;i--)sa[c[x[i]]--]=i;
    for(long long k=1;k<=n;k<<=1){
        long long num=0;
        for(long long i=n-k+1;i<=n;i++)y[++num]=i;
        for(long long i=1;i<=n;i++)if(sa[i]>k)y[++num]=sa[i]-k;
        for(long long i=1;i<=m;i++)c[i]=0;
        for(long long i=1;i<=n;i++)c[x[i]]++;
        for(long long i=2;i<=m;i++)c[i]+=c[i-1];
        for(long long i=n;i>=1;i--)sa[c[x[y[i]]]--]=y[i],y[i]=0;
        swap(x,y);
        x[sa[1]]=1;
        num=1;
        for(long long i=2;i<=n;i++){
            x[sa[i]]=(y[sa[i]]==y[sa[i-1]]&&y[sa[i]+k]==y[sa[i-1]+k])?num:++num;
        }
        if(num==n)break;
        m=num;
    }
    getHEIGHT();
}
void work(long long x,long long y){
    if(siz[x]<siz[y])swap(x,y);
    fa[y]=x;
    ans1+=siz[x]*siz[y];
    siz[x]+=siz[y];
    number[x]=max(max(number[x],number[y]),max(maxx[x]*maxx[y],minn[x]*minn[y]));
    maxx[x]=max(maxx[x],maxx[y]);
    minn[x]=min(minn[x],minn[y]);
    ans2=max(ans2,number[x]);
}
int main()
{
//    freopen("1.in","r",stdin);
//    freopen("1.out","w",stdout);
    scanf("%lld",&n);
    scanf("%s",s+1);
    for(int i=1;i<=n;i++)scanf("%lld",&a[i]);
    m=122;
    getSA();
    for(int i=1;i<=n;i++)h[height[i]].push_back(i);
    for(long long i=1;i<=n;i++){
        fa[i]=i;
        siz[i]=1;
        maxx[i]=minn[i]=a[sa[i]];
        number[i]=-inf;
    }
    for(long long i=n-1;i>=0;i--){
        for(long long j=0;j<h[i].size();j++){
            long long x=h[i][j];
            work(get(x-1),get(x));
        }
        if(ans1){
            ans[i]=ans1;
            max1[i]=ans2;
        }
    }
    for(long long i=0;i<n;i++){
        printf("%lld %lld\n",ans[i],max1[i]);
    }
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/chloris/p/11291360.html
今日推荐