zoukankan      html  css  js  c++  java
  • 洛谷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
    ",ans[i],max1[i]);
        }
        return 0;
    }
  • 相关阅读:
    equals方法
    StringBudilde
    日期
    system
    File类
    calender
    stringbuilder tostring
    File的三种构造方法
    Java入门——day52
    Java入门——day53
  • 原文地址:https://www.cnblogs.com/chloris/p/11291360.html
Copyright © 2011-2022 走看看